<dl id="opymh"></dl>

<div id="opymh"></div>
      <div id="opymh"><tr id="opymh"></tr></div>

        <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

        <em id="opymh"></em>

        <em id="opymh"><ol id="opymh"></ol></em>

              频道栏目
              首页 > 程序开发 > 移动开发 > Android > 正文
              Android开发之做一键批量卸载App功能的详细讲解
              2018-07-30 14:26:54         来源£º奋进的代码  
              收藏   我要投稿

              首先准备一部已经Root的手机£¬然后打开Android Studio£¬下面我们开始快乐的写代码吧~

              首先我们先分析具体的业务需求£º

              很简单的一个需求£¬最主要的功能就是可以卸载App£»

              同时要求可以批量卸载£»

              既然能够批量卸载£¬也就是说我们在UI交互上可以批量选择£»

              能大量展示待卸载的App¡£

              好的我们现在一步一步的来£º首先我们先解决最主要的需求£¬卸载App£¡

              有两种方式可以实现App卸载£º分为静默方式和非静默方式¡£

              什么是静默方式£¿意思就是说卸载完全是在系统后台进行的£¬不需要用户去点击确认卸载¡£非静默方式的意思显而?#20934;û£?#21368;载的时候需要用户点击确认£¬只有用户确认卸载才会卸载¡£

              我们先说非静默方式卸载£º

              非静默方式卸载的代码如下;

               public void unstallApp(String pageName){
              
                Intent uninstallIntent = new Intent();
                uninstallIntent.setAction(Intent.ACTION_DELETE);
                uninstallIntent.setData(Uri.parse("package:"+pageName));
                
                startActivityForResult(uninstall_intent,1);
              
               }

              从代码中我们就可以看出来£¬这里开启了一个活动£¬也就是所谓的应用卸载程序£¬然后把需要卸载的App包名交给它£¬它就会把这个App给卸载掉¡£这是正常的App卸载步骤¡£开启这个应用卸载程序活动后£¬页面就会跳转到卸载页面£¬然后等待用户点击确定或者取消£¬点击确定就会执行卸载程序£¬点击取消就会回?#35828;?#21407;来的活动¡£在这里我们使用了startActivityForResult()方法来开启应用卸载活动£¬目的是为了卸载完成后在回掉函数里面可以更新原来的App列表页面¡£

              非静默方式代码非常的简单£¬也非常容易理解£¬但是这里有个不足之处£¬那就是如果我们一次性需要卸载十个APP应用£¬那么页面将会跳转十次£¬同时你也需要点击十次确定£¡别忘了我们这里可是要求批量卸载£¬如果让用户去连续点击十次确定£¬这样会非常影响用户体验£¡所以非静默方式卸载在这里使用并不是很好£¬静默方式是更好的选择£¡

              静默方式卸载£º

              静默方式也就是意味着我们需要绕过安卓的界面£¬在后台执行卸载命令£¬那么怎么做呢£¿很显然£¬当然是使用命令了£¡使用命令的方式我们可以绕过安卓界面执行¡£

              这里有两种卸载App命令£º

              首先是adb命令£ºadbuninstall

              还有一个pm命令£ºpm uninstall

              我们可以看到这两种命令写法相同£¬命令的开头不同£¬那么他们具体的差别在什么地方呢£¿应该用哪一种命令方式£¿还是两种命令方式都合适呢£¿

              我先不?#30331;?#21035;£¬我们去实地的测试一下£¬首先我们先用adb命令去卸载¡£

              代码如下£º

              package com.example.uninstallapk;
              
              import android.util.Log;
              
              import java.io.DataOutputStream;
              
              /**
               * Created by 王将 on 2018/7/23.
               */
              
              
              //adb命令翻译执行类
              public class RootCmd {
               /***
                * @param command
                * @return
                */
               public static boolean exusecmd(String command) {
                Process process = null;
                DataOutputStream os = null;
                try {
              process = Runtime.getRuntime().exec("su");
              os = new DataOutputStream(process.getOutputStream());
              os.writeBytes(command + "\n");
              os.writeBytes("exit\n");
              os.flush();
              Log.e("updateFile", "======000==writeSuccess======");
              process.waitFor();
                } catch (Exception e) {
              Log.e("updateFile", "======111=writeError======" + e.toString());
              return false;
                } finally {
              try {
               if (os != null) {
                os.close();
               }
               if (process != null) {
                process.destroy();
               }
              } catch (Exception e) {
               e.printStackTrace();
              }
                }
                return true;
               }
              
               public static void unInstallApk(String pageName){
              
                exusecmd("adb uninstall "+pageName);
               }
              
              }
              

              主活动中我们调用£º

              RootCmd.unInstallApk("com.example.tset");

              把想要卸载的App包名传进去£¬运行一下£¬很快你就发现£º整个应用崩溃了£¬出现了ANR问题£¬应用无?#20174;¦¡?/p>

              好£¬我们改为pm命令试一下£¬结果发现成功了£¡

              那么现在我们分析一下为什么adb命令会导致出现ANR问题£¬而pm命令就不会出现错误¡£

              一个命令的下达£¬肯定会调用相应的方法去处理£¬只不过这个调用过程在系统的内部£¬我们外界是看不到的£¬只能得到命令执行的结果¡£就好比我们使用命令去卸载App应用£¬同样也是在内部调用了卸载方法£¬那么具体这个方法是什么£¿在哪里呢£¿下面我们就去深入的探讨一下¡£

              Android系统卸载App应用都是调用了一个类 中方法£¬不管是非静默模?#20132;?#26159;静默模式£¬这个类就是PackageInstaller类¡£当然Android系统安装App也同样是调用的它里面的方法£¬这个类功能从它的名字上就可以看出来£º打包安装程序¡£

              当然这个类我们在平常的开发中是用不到的£¬同样也是无法调用的£¬这个类同样也是一个底层调用的类¡£在这个类中我们可以?#19994;?#20855;体的卸载App方法£¬让我们看一下源码£º

              /**
                * Uninstall the given package, removing it completely from the device. This
                * method is only available to the current "installer of record" for the
                * package.
                *
                * @param packageName The package to uninstall.
                * @param statusReceiver Where to deliver the result.
                */
               public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
                uninstall(packageName, 0 /*flags*/, statusReceiver);
               }
              
               /**
                * Uninstall the given package, removing it completely from the device. This
                * method is only available to the current "installer of record" for the
                * package.
                *
                * @param packageName The package to uninstall.
                * @param flags Flags for uninstall.
                * @param statusReceiver Where to deliver the result.
                *
                * @hide
                */
               public void uninstall(@NonNull String packageName, @DeleteFlags int flags,
              @NonNull IntentSender statusReceiver) {
                uninstall(new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
               flags, statusReceiver);
               }
              
               /**
                * Uninstall the given package with a specific version code, removing it
                * completely from the device. This method is only available to the current
                * "installer of record" for the package. If the version code of the package
                * does not match the one passed in the versioned package argument this
                * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
                * uninstall the latest version of the package.
                *
                * @param versionedPackage The versioned package to uninstall.
                * @param statusReceiver Where to deliver the result.
                */
               public void uninstall(@NonNull VersionedPackage versionedPackage,
              @NonNull IntentSender statusReceiver) {
                uninstall(versionedPackage, 0 /*flags*/, statusReceiver);
               }
              
               /**
                * Uninstall the given package with a specific version code, removing it
                * completely from the device. This method is only available to the current
                * "installer of record" for the package. If the version code of the package
                * does not match the one passed in the versioned package argument this
                * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
                * uninstall the latest version of the package.
                *
                * @param versionedPackage The versioned package to uninstall.
                * @param flags Flags for uninstall.
                * @param statusReceiver Where to deliver the result.
                *
                * @hide
                */
               @RequiresPermission(anyOf = {
              Manifest.permission.DELETE_PACKAGES,
              Manifest.permission.REQUEST_DELETE_PACKAGES})
               public void uninstall(@NonNull VersionedPackage versionedPackage, @DeleteFlags int flags,
              @NonNull IntentSender statusReceiver) {
                Preconditions.checkNotNull(versionedPackage, "versionedPackage cannot be null");
                try {
              mInstaller.uninstall(versionedPackage, mInstallerPackageName,
                flags, statusReceiver, mUserId);
                } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
                }
               }

              这个是PackageInstaller类中的四个uninstall()方法£¬具体的功能就是卸载App应用¡£

              当然这四个方法用于卸载不同状态的应用£¬具体的使?#20204;?#30475;官方给出的描述文档£¬这里不再具体的做出分析¡£

              现在我们知道了卸载App调用的是PackageInstaller类的uninstall()方法£¬那么这个和命令的方式有什么关?#30340;Ø£?#25105;们看一下PackageInstaller类的所处路径你就明白了£¬PackageInstaller类的所处路径为/android/content/pm/PackageInstaller.java£¬具体在博主这里的完整路径为£º

              \

              很明显£¬在/pm路径下¡£pm全称package manager£¬意思包的管理者£¬pm命令说白了就是包管理命令£¬进一步说£¬只有使用pm命令才会调用/pm路径下的底层方法£¬也就是说才会执行包文件的操作¡£这下你明白为什么使用adb会导致ANR问题了吧£¬因为程序?#20063;?#21040;执行方法啊£¡

              好了£¬现在我们解决了最重要的需求£¬静默卸载App£¬那么接下来的需求就很简单实现了£¬批量卸载£¬批量选择£¬这里直接使用一个循环不停的执行卸载命令就好了¡£按照这个思路我们开始写代码¡£

              首先是界面UI部分£º

              
              
              
               
                
              
                
               

              使用ScrollView嵌套一个LinearLayout布局来实现App列表£¬其中单个的App信息使用动态加载的形式添加¡£

              下面是一个App信息子布局£º

              
              
              
               
               
              

              很简单£¬两个控件组成£¬一个ChexBox控件提供勾选£¬一个TextView用来展示App的标签¡£

              接下来我们就需要写主活动中的逻辑性操作了£º

              首先贴上我们的MainActivity代码£º

              public class MainActivity extends AppCompatActivity {
              
               LinearLayout linearLayout;
              
               List pages=new ArrayList<>();
               List views=new ArrayList<>();
              
               ProgressDialog progressDialog;
              
               List packageInfos=new ArrayList<>();
              
               @Override
               protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
              
                linearLayout = (LinearLayout) findViewById(R.id.linear1);
              
                Button button=(Button) findViewById(R.id.start_delete);
              
              
                PackageManager packageManager=getPackageManager();
                packageInfos=packageManager.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
              
                int id=0;
                for (PackageInfo packageInfo:packageInfos){
              String str=packageInfo.applicationInfo.loadLabel(getPackageManager()).toString();
              linearLayout.addView(getChoiceView(linearLayout,str,id));
              id++;
                }
              
                button.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
              
               new DEleteApk().execute();
              }
                });
               }
              
               private View getChoiceView(LinearLayout root, final String pageName, int id){
                final View view = LayoutInflater.from(this).inflate(R.layout.choice_layout, root, false);
              
                final CheckBox checkBox=(CheckBox) view.findViewById(R.id.page_id);
                final TextView textView=(TextView) view.findViewById(R.id.page_name);
              
                view.setTag(id);
              
                checkBox.setTag(view);
              
                checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
              @Override
              public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
              
               if (isChecked){
              
                views.add((View) checkBox.getTag());
                pages.add((int) view.getTag());
              
               }else {
              
                View view1=(View) checkBox.getTag();
                views.remove(view1);
                pages.remove(getIndexPages((int) view1.getTag()));
              
               }
              
              }
                });
              
                textView.setText(pageName);
              
                return view;
               }
              
               public int getIndexPages(int id){
                int index=0;
                int j=0;
              
                for (int i:pages){
              if (i==id){
               index=j;
               break;
              }
              j++;
                }
              
                return index;
               }
              
               class DEleteApk extends AsyncTask {
              
                @Override
                protected void onPreExecute() {
              progressDialog=new ProgressDialog(MainActivity.this);
              progressDialog.setTitle("正在卸载中");
              progressDialog.setMessage("请稍后...");
              progressDialog.setCancelable(true);
              progressDialog.show();
                }
              
                @Override
                protected Object doInBackground(Object[] objects) {
              for (int id:pages){
               RootCmd.unInstallApk(packageInfos.get(id).packageName);
              }
              return true;
                }
              
                @Override
                protected void onPostExecute(Object o) {
              progressDialog.dismiss();
              
              pages.removeAll(pages);
              for (View view:views){
               linearLayout.removeView(view);
              }
              views.removeAll(views);
                }
               }
              }

              在代码中有?#35762;?#20998;讲解一下£º

              首先是getChoiceView()方法¡£在这个方法中我们主要获取用户勾选的App是哪些¡£当用户点击勾选的时候£¬我们就?#35759;?#24212;App的下标值给存下来£¬同时存下来的还有相应的子View£¬存放子View的目的是为了在卸载完成之后更新我们的App列表¡£在用户点击取消勾选£¬我们还需要把之前存放的相关信息给移除掉£¬确保卸载的都是用户最终确定删除的¡£

              存好了相应的信息£¬下面就是执行pm命令部分¡£在这里我使用线程来开启pm命令£¬可以很清楚地看到£¬在这里我使用了AsyncTask 框架¡£在线程开启前£¬也就是pm命令开始之前£¬我们弹出一个ProgressDialog£¬目的就是告诉用户正在卸载请稍等£¬因为pm命令执行起来到结束会需要一定的时间£»然后就开始执行pm命令£¬使用循环挨着挨卸载List中用户选定的App£¬执行结束后关闭ProgressDialog£¬然后清空我们的Liset£¬同时还要更改我们的UI界面¡£

              这里选用AsyncTask 框架有一个好处£¬那就是可以明确的知道命令执行结束的时间£¬在命令结束之后更改UI¡£如果不使用AsyncTask 框架£¬那么就比较难以掌握pm命令执行结束的时候£¬毕竟这个也没有什么相关的回掉函数£¬在结束后UI处理上难以下手¡£使用AsyncTask 框架后£¬就不需要担心这个问题£¬执行结束后自然会执行收尾工作£¬这样更新IUI就方便多了¡£

              好了£¬本篇文章到此结束¡£有需要引用的请标明出处£¬谢谢£¡

              点击复制链接 与好友分享!回本站首页
              上一篇£ºAndroid开发之引用外部数据库操作讲解
              下一篇£ºAndroid开发之占?#29615;?#24212;用?#36947;?#35299;析
              相关文章
              图文推荐
              点击排行

              关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报?#34892;?/a>

              版权所有: 红黑联盟--致力于做实用的IT技术学习网站

              ¼«ËÙ·ÉͧºÃ¼Ù
              <dl id="opymh"></dl>

              <div id="opymh"></div>
                  <div id="opymh"><tr id="opymh"></tr></div>

                    <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

                    <em id="opymh"></em>

                    <em id="opymh"><ol id="opymh"></ol></em>

                          <dl id="opymh"></dl>

                          <div id="opymh"></div>
                              <div id="opymh"><tr id="opymh"></tr></div>

                                <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

                                <em id="opymh"></em>

                                <em id="opymh"><ol id="opymh"></ol></em>