2013年8月29日星期四

Android中后台线程如何与UI线程交互

我想关于这个话题已经有很多前辈讨论过了。今天算是一次学习总结吧。
在android的设计思想中,为了确保用户顺滑的操作体验。一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务。因此我们必须要重新开启一个后台线程运行这些任务。然而,往往这些任务最终又会直接或者间接的需要访问和控制UI控件。例如访问网络获取数据,然后需要将这些数据处理显示出来。就出现了上面所说的情况。原本这是在正常不过的现象了,但是android规定除了UI线程外,其他线程都不可以对那些UI控件访问和操控。为了解决这个问题,于是就引出了我们今天的话题。Android中后台线程如何与UI线程交互。

据我所知android提供了以下几种方法,用于实现后台线程与UI线程的交互。
1、handler
2、Activity.runOnUIThread(Runnable)
3、View.Post(Runnable)
4、View.PostDelayed(Runnabe,long)
5、AsyncTask

方法一:handler
handler是android中专门用来在线程之间传递信息类的工具。
要讲明handler的用法非常简单,但是我在这里会少许深入的讲一下handler的运行机制。
为了能够让handler在线程间传递消息,我们还需要用到几个类。他们是looper,messageQueue,message。
这里说的looper可不是前段时间的好莱坞大片环形使者,他的主要功能是为特定单一线程运行一个消息环。一个线程对应一个looper。同样一个looper对应一个线程。这就是所谓的特定单一。一般情况下,在一个线程创建时他本身是不会生产他特定单一的looper的(主线程是个特例)。因此我们需要手动的把一个looper与线程相关联。其方法只需在需要关联的looper的线程中调用Looper.prepare。之后我们再调用Looper.loop启动looper。
说了这么多looper的事情,到底这个looper有什么用哪。其实之前我们已经说到了,他是为线程运行一个消息环。具体的说,在我们将特定单一looper与线程关联的时候,looper会同时生产一个messageQueue。他是一个消息队列,looper会不停的从messageQuee中取出消息,也就是message。然后线程就会根据message中的内容进行相应的操作。
那么messageQueue中的message是从哪里来的哪?那就要提到handler了。在我们创建handler的时候,我们需要与特定的looper绑定。这样通过handler我们就可以把message传递给特定的looper,继而传递给特定的线程。在这里,looper和handler并非一一对应的。一个looper可以对应多个handler,而一个handler只能对应一个looper(突然想起了一夫多妻制,呵呵)。这里补充一下,handler和looper的绑定,是在构建handler的时候实现的,具体查询handler的构造函数。
在我们创建handler并与相应looper绑定之后,我们就可以传递message了。我们只需要调用handler的sendMessage函数,将message作为参数传递给相应线程。之后这个message就会被塞进looper的messageQueue。然后再被looper取出来交给线程处理。
这里要补充说一下message,虽然我们可以自己创建一个新的message,但是更加推荐的是调用handler的obtainMessage方法来获取一个message。这个方法的作用是从系统的消息池中取出一个message,这样就可以避免message创建和销毁带来的资源浪费了(这也就是算得上重复利用的绿色之举了吧)。
突然发现有一点很重要的地方没有讲到,那就是线程从looper收到message之后他是如何做出响应的嘞。其实原来线程所需要做出何种响应需要我们在我们自定义的handler类中的handleMessage重构方法中编写。之后才是之前说的创建handler并绑定looper。
好吧说的可能哟点乱,总结一下利用handler传递信息的方法。
假设A线程要传递信息给B线程,我们需要做的就是
1、在B线程中调用Looper.prepare和Looper.loop。(主线程不需要)
2、 编写Handler类,重写其中的handleMessage方法。
3、创建Handler类的实例,并绑定looper
4、调用handler的sentMessage方法发送消息。
到这里,我们想handler的运行机制我应该是阐述的差不多了吧,最后再附上一段代码,供大家参考。
复制代码
 1 public class MyHandlerActivity extends Activity {
 2      TextView textView;
 3      MyHandler myHandler;
 4  
 5      protected void onCreate(Bundle savedInstanceState) {
 6          super.onCreate(savedInstanceState);
 7          setContentView(R.layout.handlertest);
 8  
 9          //实现创建handler并与looper绑定。这里没有涉及looper与
            //线程的关联是因为主线程在创建之初就已有looper
10          myHandler=MyHandler(MyHandlerActivitythis.getMainLooper());
11          textView = (textView) findViewById(R.id.textView);
12         
13          MyThread m = new MyThread();
14          new Thread(m).start();
15      }
16  
17  
18      class MyHandler extends Handler {
19          public MyHandler() {
20          }
21  
22          public MyHandler(Looper L) {
23              super(L);
24          }
25  
26          // 必须重写这个方法,用于处理message
27          @Override
28          public void handleMessage(Message msg) {
29              // 这里用于更新UI
30              Bundle b = msg.getData();
31              String color = b.getString("color");
32              MyHandlerActivity.this.textView.setText(color);
33          }
34      }
35  
36      class MyThread implements Runnable {
37          public void run() {
38              //从消息池中取出一个message
39              Message msg = myHandler.obtainMessage();
40              //Bundle是message中的数据
41              Bundle b = new Bundle();
42              b.putString("color", "我的");
43              msg.setData(b);
44              //传递数据
45              myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
46          }
47      }
复制代码

参考资料:
http://developer.android.com/reference/android/os/Handler.html
http://developer.android.com/reference/android/os/Looper.html
http://developer.android.com/reference/android/os/Message.html
http://www.cnblogs.com/dawei/archive/2011/04/09/2010259.html
http://rxwen.blogspot.com/2010/08/looper-and-handler-in-android.html
http://www.cnblogs.com/android007/archive/2012/05/10/2494766.html

方法二:Activity.runOnUIThread(Runnable)
 这个方法相当简单,我们要做的只是以下几步
1、编写后台线程,这回你可以直接调用UI控件
2、创建后台线程的实例
3、调用UI线程对应的Activity的runOnUIThread方法,将后台线程实例作为参数传入其中。
注意:无需调用后台线程的start方法
方法三:View.Post(Runnable)
 该方法和方法二基本相同,只是在后台线程中能操控的UI控件被限制了,只能是指定的UI控件View。方法如下
1、编写后台线程,这回你可以直接调用UI控件,但是该UI控件只能是View
2、创建后台线程的实例
3、调用UI控件View的post方法,将后台线程实例作为参数传入其中。
方法四:View.PostDelayed(Runnabe,long)
该方法是方法三的补充,long参数用于制定多少时间后运行后台进程 
方法五:AsyncTask
AsyncTask是一个专门用来处理后台进程与UI线程的工具。通过AsyncTask,我们可以非常方便的进行后台线程和UI线程之间的交流。
那么AsyncTask是如何工作的哪。
AsyncTask拥有3个重要参数
1、Params 
2、Progress
3、Result
Params是后台线程所需的参数。在后台线程进行作业的时候,他需要外界为其提供必要的参数,就好像是一个用于下载图片的后台进程,他需要的参数就是图片的下载地址。
Progress是后台线程处理作业的进度。依旧上面的例子说,就是下载图片这个任务完成了多少,是20%还是60%。这个数字是由Progress提供。
Result是后台线程运行的结果,也就是需要提交给UI线程的信息。按照上面的例子来说,就是下载完成的图片。
AsyncTask还拥有4个重要的回调方法。
1、onPreExecute
2、doInBackground
3、onProgressUpdate
4、onPostExecute
onPreExecute运行在UI线程,主要目的是为后台线程的运行做准备。当他运行完成后,他会调用doInBackground方法。
doInBackground运行在后台线程,他用来负责运行任务。他拥有参数Params,并且返回Result。在后台线程的运行当中,为了能够更新作业完成的进度,需要在doInbackground方法中调用PublishProgress方法。该方法拥有参数Progress。通过该方法可以更新Progress的数据。然后当调用完PublishProgress方法,他会调用onProgressUpdate方法用于更新进度。
onProgressUpdate运行在UI线程,主要目的是用来更新UI线程中显示进度的UI控件。他拥有Progress参数。在doInBackground中调用PublishProgress之后,就会自动调onProgressUpdate方法
onPostExecute运行在UI线程,当doInBackground方法运行完后,他会调用onPostExecute方法,并传入Result。在onPostExecute方法中,就可以将Result更新到UI控件上。
明白了上面的3个参数和4个方法,你要做的就是
1、编写一个继承AsyncTask的类,并声明3个参数的类型,编写4个回调方法的内容。
2、然后在UI线程中创建该类(必须在UI线程中创建)。
3、最后调用AsyncTask的execute方法,传入Parmas参数(同样必须在UI线程中调用)。
这样就大功告成了。
另外值得注意的2点就是,千万不要直接调用那四个回调方法。还有就是一个AsyncTask实例只能执行一次,否则就出错哦。
以上是AsyncTask的基本用法,更加详细的内容请参考android官方文档。最后附上一段代码,供大家参考。

复制代码
 1 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> 
 2 //在这里声明了Params、Progress、Result参数的类型
 3 {
 4     //因为这里不需要使用onPreExecute回调方法,所以就没有加入该方法
 5     
 6     //后台线程的目的是更具URL下载数据
 7      protected Long doInBackground(URL... urls) {
 8          int count = urls.length;//urls是数组,不止一个下载链接
 9          long totalSize = 0;//下载的数据
10          for (int i = 0; i < count; i++) {
11              //Download是用于下载的一个类,和AsyncTask无关,大家可以忽略他的实现
12              totalSize += Downloader.downloadFile(urls[i]);
13              publishProgress((int) ((i / (float) count) * 100));//更新下载的进度
14              // Escape early if cancel() is called
15              if (isCancelled()) break;
16          }
17          return totalSize;
18      }
19 
20      //更新下载进度
21      protected void onProgressUpdate(Integer... progress) {
22          setProgressPercent(progress[0]);
23      }
24 
25      //将下载的数据更新到UI线程
26      protected void onPostExecute(Long result) {
27          showDialog("Downloaded " + result + " bytes");
28      }
29  }
30  
复制代码



 有了上面的这个类,接下你要做的就是在UI线程中创建实例,并调用execute方法,传入URl参数就可以了。

 参考资料
http://developer.android.com/reference/android/os/AsyncTask.html
http://www.cnblogs.com/dawei/archive/2011/04/18/2019903.html

这上面的5种方法各有优点。但是究其根本,其实后面四种方法都是基于handler方法的包装。在一般的情形下后面四种似乎更值得推荐。但是当情形比较复杂,还是推荐使用handler。
最后补充一下,这是我的第一篇博客。存在很多问题请大家多多指教。尤其是文中涉及到内容,有严重的技术问题,大家一定要给我指明啊。拜托各位了。

如需转发请注明原文地址。

Android使用后台线程提高用户体验

    当应用程序启动时,系统会为应用程序创建一个主线程(main)或者叫UI线程,它负责分发事件到不同的组件,包括绘画事件。完成你的应用程序与android UI组件交互。例如,当您触摸屏幕上的一个按钮时,UI线程会把触摸事件分发到组件上,更改状态并加入事件队列,UI线程会分发请求和通知到各个组件,完成相应的动作。
     单线程模型的性能是非常差的,除非你的应用程序相当的简单,特别是当所有的操作都在主线程中执行,比如访问网络或数据库之类的耗时操作将会导致用户界面锁定,所有的事件将不能分发,应用程序就像死了一样,更严重的是当超过5秒时,系统就会弹出“应用程序无响应”的对话框。显然这会造成很差的用户体验,所以我们需要保证主线程(UI线程)不被锁住,如果有耗时的操作,我们需要把它放到一个单独的后台线程中执行。
      通过后台线程来提高用户体验的方式很多,一个最简单的方式就是在进行耗时操作的地方新开一个线程,用该线程来处理耗时操作,示例代码如下:
[java] view plaincopyprint?
public void onClick(View v) {  
    new Thread(new Runnable() {  
        public void run() {  
            // 执行耗时操作  
        }  
    }).start();  
}  
      起初,上面的代码似乎是一个很好的解决方案,因为它不会锁住用户界面线程。然面不幸的是,它违反了用户界面单线程模型:android的用户界面工具包不是线程安全的,只能在UI线程中操作它。android提供了几种方法来从其他线程访问UI线程。下面是一个较全面的列表:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
      一般情况下,我们会用Handler做UI线程的修改,示例代码如下:
[java] view plaincopyprint?
private ProgressDialog progressDialog;  
private Handler myHandler = new Handler(){  
    @Override  
    public void handleMessage(Message msg) {  
        progressDialog.dismiss();  
        super.handleMessage(msg);  
    }  
};  
public void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.main);  
         
    progressDialog = new ProgressDialog(MainActivity.this);  
    progressDialog.setMessage("Loading…");  
    progressDialog.show();  
        
    new Thread(new Runnable() {  
        @Override  
        public void run() {  
            //这里作比较耗时的工作,暂时用线程休眠2秒作替代。  
            try {  
                Thread.sleep(4*1000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            myHandler.sendMessage(myHandler.obtainMessage());  
        }  
    }).start();  
}  
首先显示一个ProgressDialog做界面友好提示,然后新开线程做耗时操作,最后调用handler的sendMessage,唤醒Handler。

      除了上述的几种方法之外,1.5和更高版本的Android平台提供了一个实用类称为AsyncTask,简化了长时间运行的任务,需要与用户界面的交互。AsyncTask的目标是要为你的线程提供管理服务,示例代码如下:
[java] view plaincopyprint?
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> {  
    @Override  
    protected Void doInBackground(Void… params) {  
        //耗时操作,  
        try {  
            Thread.sleep(4*1000);  
        } catch (InterruptedException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        return null;  
    }  
    @Override  
    protected void onPostExecute(Void result) {  
        //作UI线程的修改。  
        progressDialog.dismiss();  
        super.onPostExecute(result);  
    }     
}  

以下是AsyncTask的简要使用方法:
    •您可以指定三个参数类型,泛型参数,进度值(执行过程中返回的值)和最终值(执行完返回的值)。
    •该方法doInBackground()自动执行工作线程(后台线程)
    •onPreExecute(),onPostExecute()和onProgressUpdate()都是在UI线程调用
    •由doInBackground返回的值()发送到onPostExecute()
    •您可以在执行doInBackground()时调用publishProgress()然后在UI组程中执行onProgressUpdate()。
    •您可以从任何线程随时取消任务
不管你是否使用AsyncTask,时刻牢记单一线程模型的两条规则:
    1、不要锁住用户界面。
    2、确保只在UI线程中访问android用户界面工具包中的组件。

THE END!

Android读取联系人姓名、电话

一、《Android应用开发揭秘》代码的问题
在该书上,
int nameIndex  = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
String contact = cursor.getString(nameIndex);
两句是可以读到联系人的名字的,而
//取得电话号码
            int numberFieldColumnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);   
            String number = cursor.getString(numberFieldColumnIndex);
会发生错误,因为2.0后,读取电话号码的API发生了改变

二、代码
   1: /***********************************/

   2: /*

   3:  *联系人的名字、电话号码的读取与显示

   4: */

   5: /***********************************/

   6: package com.Joe.Android.Examples_03_02;

   7:  

   8: import android.app.Activity;

   9: import android.content.ContentResolver;

  10: import android.database.Cursor;

  11: import android.os.Bundle;

  12: import android.provider.ContactsContract;

  13: import android.provider.ContactsContract.PhoneLookup;

  14: import android.widget.TextView;

  15:  

  16: public class mainActivity extends Activity {

  17:     /** Called when the activity is first created. */

  18:     @Override

  19:     public void onCreate(Bundle savedInstanceState) {

  20:         TextView tv = new TextView(this);

  21:         String string = "";

  22:         super.onCreate(savedInstanceState);

  23:         //得到contentresolver对象

  24:         ContentResolver cr = getContentResolver();

  25:         //取得电话本中开始一项的光标,必须先moveToNext()

  26:         Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI,null, null, null, null);

  27:         while(cursor.moveToNext()){

  28:             //取得联系人的名字索引

  29:             int nameIndex  = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);

  30:             String contact = cursor.getString(nameIndex);

  31:             string += (contact+":"+"/n");

  32:             

  33:             //取得联系人的ID索引值

  34:             String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)); 

  35:             //查询该位联系人的电话号码,类似的可以查询email,photo

  36:             Cursor phone = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,

  37:                     ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "  

  38:                     + contactId, null, null);//第一个参数是确定查询电话号,第三个参数是查询具体某个人的过滤值

  39:             //一个人可能有几个号码

  40:             while(phone.moveToNext()){

  41:                 String strPhoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));

  42:                 string += (strPhoneNumber+"/n");

  43:             }

  44:             phone.close();

  45:         }

  46:         cursor.close();

  47:         //设置显示内容

  48:         tv.setText(string);

  49:         //显示

  50:         setContentView(tv);      

  51:         //setContentView(R.layout.main);

  52:     }

  53: }

、注意点
最后要在AndroidManifest.xml的application之外加上
    <uses-permission
    android:name="android.permission.READ_CONTACTS">
    </uses-permission>

移动APP隐私乱象:隐私越轨背后的商业牟利

如何解决开放性与安全性的矛盾,这或许是安卓产业链业者所需要思考的问题。
据中国互联网数据中心数据显示,在国内各类Android市场下载量前1400位的APP内,有66.9%的智能手机移动应用在抓取用户隐私数据,其中,34.5%的移动应用有“隐私越轨”行为。
“隐私越轨”行为是指APP抓取用户隐私信息并非APP服务功能所必需。
报告认为,61%的短信记录读取权限,73%的通话记录读取权限,是移动应用功能中不必要的权限,即存在“隐私越轨”行为。
《第一财经日报》记者采访中发现,一些第三方应用通过出售信息、绕开安全应用与硬件厂商联合牟利的现象也大量存在。
“隐私越轨”愈演愈烈
腾讯移动安全实验室专家陆兆华对《第一财经日报》表示,APP读取用户隐私正在被大众逐步地认知,但这一现象也在进一步抬头,向窃取用户核心隐私的趋势正在加强。
截至2013年第一季度,据腾讯移动安全实验室软件池数据分析显示,含广告插件的APP总数为2038139个,在所有软件样本中占比43.5%;广告插件类APP已成为手机用户隐私的巨大威胁。
上述机构还抽取了近470万个软件包进行代码扫描,分析软件中是否同时含有申请隐私权限以及读取隐私信息的API结果相关代码问题。统计发现:有读取用户隐私权限的相关操作的软件比例达到71%,即有超过333万个软件包同时申请了隐私权限,且含有读取用户的隐私权限相关操作的代码。
其中在这333万个软件中,读取设备识别信息与本机手机号的占比61.75%与28.33%;读取地理位置信息的占比28.27%;读取通讯录的占比10.29%,读取短信的占比4.22%;打开手机摄像头与录音器的分别占据4.15%与3.75%的比例;读取通话记录的占比2.86%;读取浏览器书签的占比7.93%。
陆兆华表示,一款应用是否属于过度读取隐私权限,主要是看该款软件读取该项隐私权限是基于什么目的,即是否属于功能必需的范畴之内,反之则属于“隐私越轨”行为。
一般而言,安全软件、系统管理软件、通讯软件、社交软件等软件针对用户的通讯录等隐私读取是在合理的权限之内,而LBS、手机地图软件、导航软件等针对用户的地理位置读取与定位也属于合法的权限范畴等。但例如一款游戏软件或壁纸软件需要读取用户的通讯录和短信信息则大可不必。
360方面人士也对记者表示,2013年上半年的热门应用中,一款应用拥有1~5个隐私权限的应用比例达64.5%,拥有6~10个隐私权限的应用占28%,11个以上权限也要占4.9%,不申请任何隐私权限的仅为2.6%。
以热门手机游戏“神庙逃亡”为例,腾讯手机管家针对在Google Play上下载的官方正版Temple Run(“神庙逃亡”)、含广告插件版Temple Run(以下简称”神庙逃亡”)、被植入了病毒代码的伪 “神庙逃亡”三个版本的软件权限读取情况进行了对比研究,结果统计发现,官方正版的“神庙逃亡”没有读取隐私权限;而含广告插件的“神庙逃亡”读取了手机号、GPS、IMEI和IMSI、拍照、浏览器书签、弹通知栏通知共6项权限;被打包了“病毒代码”的伪“神庙逃亡”则读取了11项权限,在广告插件类“神庙逃亡”读取的6项权限的基础上,还获取了联系人、通话记录、发送短信、拨打电话这4项用户核心隐私权限,用户的关键隐私遭受严重威胁。
“隐私”背后的商业牟利
这些被泄露的用户隐私,变现的渠道包括用户隐私信息的出售,以及自身的广告推广。而获得用户隐私信息的买家,则可能用于垃圾短信、骚扰甚至诈骗电话等,或者为广告公司牟利。
陆兆华称,由于收集地理位置、设备识别信息、本地手机号可面向固定而稳定的手机用户群精准匹配与投放相应的广告,并进行有效的用户消费行为分析,符合部分急功近利的广告商的利益诉求,APP开发者也可以因此而获取广告分成,因而获取这类信息成为某些不正规广告商与APP开发者共同的核心利益。
而与此同时,广告插件类APP读取联系人、通讯录等核心隐私的现象也大规模存在。
联想移动终端事业部软件产品经理陈宇对《第一财经日报》表示,对第三方应用开发者来说,他们希望调用的用户权限越多越好。
调用用户权限越多,就越了解用户信息,越接近ROM(存放手机固件代码的存储器,近似于操作系统)的价值,其商业价值就越大。
通过查看一款手机应用调用了哪些权限,如果这些权限不是拿去出售而是自用,也可以大致了解该应用未来可能涉及的一些商业模式。
本报记者发现,某知名手机输入法应用读取的用户权限包括5种,分别是:获取短信内容、获取联系人、获取通话记录、手机定位以及获取手机识别码。
据该软件一名内部人士称,“获取手机识别码”基本是每一个手机应用都会调用的权限,主要为了获取该手机的型号来进行应用适配,以及本地手机号码作为登录账户;而“获取短信内容”,是为了方便让输入法做到快捷输入;“获取联系人”是为了导入用户的好友联系人来优化词库,也是为了方便用户在手机上的便捷输入;而“手机定位”则是为了扩充输入法应用基于用户流量的商业模式,比如你用输入法敲打“餐馆”这个词,输入法可能会基于你所在的地理位置来推荐附近的餐厅。
但上述人士对一款输入法应用为何要“获取通话记录”则未做更多解释。据调查,调用“获取通话记录”权限的热门应用还包括百度地图、微信、手机QQ等。
金山安全反病毒工程师李铁军对《第一财经日报》表示,一些应用调用“获取通话记录”权限主要是为了获悉用户的通话状态,在用户来电话时,软件会采取相应的动作。例如,音乐类应用,当用户接电话时,需要暂停音乐播放。而一些语音类应用也可能会调用该权限,但如果是其他种类应用调用该权限的目的就不好说了。
还有一些应用还调取了“发送短信”的权限。例如,天气类工具应用墨迹天气,可能是出于功能上对天气服务的短信推送需要。而其他的一些应用调取该权限,也可能是需要通过发送短信来认证注册。但在申请得到了该权限后,第三方应用未来会否涉足非法行为,这就依赖于监管了。
为了获得收益,一些应用商店在热门软件中植入恶意广告和病毒木马,并通过应用软件升级来切换成自己开发的“伪应用”的事情也屡有发生。此外,今年1月,通过MDK后门程序,控制者可以随时窃取并上传用户短信内容、私人照片和通讯录,并在后台悄悄下载大量软件,也会消耗大量手机流量。
陈宇表示,目前一些对外宣传可以屏蔽第三方应用权限的安全软件,也并非能做到真正地屏蔽。例如调用“获取手机定位”权限的应用,如果安全软件直接屏蔽掉该功能可能导致用户无法启动该应用,而一些安全软件的做法就是通过给该应用输出一些虚拟的地理位置信息,来达到保护用户隐私的目的。
为了能更多地获取用户信息,一些手机应用干脆绕开安全软件直接与手机硬件厂商合作。
有要求匿名的业内人士对记者表示,这是因为,在PC端,由于微软开放了Admin权限(用户管理者权限),因此一些桌面客户端可以将该权限拿走,而屏蔽竞争对手的客户端,于是有了3Q大战的爆发;但在手机移动端,只有手机硬件厂商才有基于Android系统的用户管理者权限(ROOT),而其他的第三方应用包括第三方基于Android系统之上的ROM开发商都没有该权限,因此手机硬件厂商享有最高的用户安全级别。
去年6月,工信部推出《关于加强移动智能终端进网管理的通知》(征求意见稿),提出要对提供APP的第三方平台实行备案制,对APP应用实行实名制。但也有观点认为,对数以百万的APP应用,备案制显然难以操作,并给开发者带来额外成本。保护用户隐私已经成为移动互联网普及的待解难题。制图/郑勤韬