精华内容
下载资源
问答
  • 关于安卓root过手机静默安装与卸载

    千次阅读 2015-05-09 14:53:51
    用到一个工具类AndroidCommon 详细了解地址:... 走下思路,首先静默安装要用到adb命令,所以手机必须root(至于360手机助手静默安装,无需root也可以静默安装,具体我

        用到一个工具类AndroidCommon  详细了解地址:http://www.open-open.com/lib/view/open1385174381198.html   ,其中为我们提供了root后静默安装的工具类PackageUtils,其中包含安装与卸载。

       走下思路,首先静默安装要用到adb命令,所以手机必须root(至于360手机助手静默安装,无需root也可以静默安装,具体我也不清楚,哪位大牛清楚希望可以探讨下),在adb命令中执行pm install -r即可,具体可以参考AndroidCommon提供的类。工具类PackageUtil.sinstallSilent(context, path)静默安装与PackageUtil.uninstallSilent(this, path)静默卸载,都有返回值,返回值为1说明是成功的,具体返回值PackageUtil中有就不多说了 如果返回值不是1的话,可以执行普通的安装方法,接下来看代码

    demo中只将AndroidCommon的工具类打包成Lib,可直接看源码

     demo下载地址  http://download.csdn.net/detail/u012303938/8698171

       MainActivity.class  代码中的apk下载地址可能会过期,如果不能下载,换个apk下载地址即可

    package com.example.update;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.DefaultHttpClient;
    
    import cn.trinea.android.common.util.PackageUtils;
    import android.app.Activity;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Environment;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends Activity{
    	private int percent;
    	private Button button1,button2;
    	private TextView textView1;
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		// TODO Auto-generated method stub
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		button1=(Button) findViewById(R.id.button1);
    		button2=(Button) findViewById(R.id.button2);
    		textView1=(TextView) findViewById(R.id.textView1);
    		button1.setOnClickListener(new OnClickListener() {
    			
    			@Override
    			public void onClick(View arg0) {
    				// TODO Auto-generated method stub
    				loadApks();
    			}
    		});
    		button2.setOnClickListener(new OnClickListener() {
    			
    			@Override
    			public void onClick(View arg0) {
    				// TODO Auto-generated method stub
    				int  l=PackageUtils.uninstallSilent(MainActivity.this, "com.example.callphone");
    				 if(l!=1){
    					 Uri packageURI = Uri.parse("package:" + "com.example.callphone");  
    			            Intent uninstallIntent = new Intent(Intent.ACTION_DELETE,packageURI);  
    			            uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
    			            startActivity(uninstallIntent); 
    					}
    			}
    		});
    	}
    	private void loadApks() {
    		// TODO Auto-generated method stub
    		new Thread(){
    			public void run() {
    				HttpClient client=new DefaultHttpClient();
    				String url="http://121.42.15.80:800/callphone.apk";
    				HttpGet get=new HttpGet(url);;
    				
    				HttpResponse response=null;
    				try {
    					response=client.execute(get);
    					HttpEntity entity=response.getEntity();
    					long length=entity.getContentLength();
    					int count=0;
    					InputStream is=entity.getContent();
    					FileOutputStream outputStream=null;
    					if(is!=null){
    						File file=new File(Environment.getExternalStorageDirectory(),"ybds.apk");
    						outputStream=new FileOutputStream(file);
    						byte[]  bt=new byte[1024];
    						int len=-1;
    
    						while((len=is.read(bt))!=-1){
    							outputStream.write(bt, 0, len);
    							count+=len;
    							if((int)count*100/length>percent){
    								percent=(int)(100*count/length);
    								((Activity) MainActivity.this).runOnUiThread(new Runnable() {
    																		
    																		@Override
    																		public void run() {
    																			// TODO Auto-generated method stub
    																			//roundprogressbar.setProgress(percent);
    																			textView1.setText(String.valueOf(percent));
    																		}
    																	});
    																
    							}
    						}
    						outputStream.flush();
    						if(outputStream!=null){
    							outputStream.close();
    						}
    					}
    					inStall();
    					//jimodown();
    					//installDown();
    				} catch (ClientProtocolException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			};
    		}.start();
    	}
    
    	protected void inStall() {
    		// TODO Auto-generated method stub
    
    
    		//	File files=new File(Environment.getExternalStorageDirectory(),"TestDemo.apk");
    		String path =Environment.getExternalStorageDirectory()
    				.getPath() +"/ybds.apk" ;
    		
    		int i=PackageUtils.installSilent(this, path);
    		if(i!=1){
    			File file = new File(path);  
    			if(!file.exists()){  
    				return ;   
    			}
    			Intent intent = new Intent();  
    			intent.setAction("android.intent.action.VIEW");  
    			intent.addCategory("android.intent.category.DEFAULT");  
    			intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
    			intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");  
    			startActivity(intent);
    		}
    			
    	}
    }


    展开全文
  • 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好。虽说这种方式看上去不打搅用户,但是却存在着...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149


    之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好。虽说这种方式看上去不打搅用户,但是却存在着一个问题,因为Android系统会在安装界面当中把程序所声明的权限展示给用户看,用户来评估一下这些权限然后决定是否要安装该程序,但如果使用了静默安装的方式,也就没有地方让用户看权限了,相当于用户被动接受了这些权限。在Android官方看来,这显示是一种非常危险的行为,因此静默安装这一行为系统是不会开放给开发者的。


    但是总是弹出一个安装对话框确实是一种体验比较差的行为,这一点Google自己也意识到了,因此Android系统对自家的Google Play商店开放了静默安装权限,也就是说所有从Google Play上下载的应用都可以不用弹出安装对话框了。这一点充分说明了拥有权限的重要性,自家的系统想怎么改就怎么改。借鉴Google的做法,很多国内的手机厂商也采用了类似的处理方式,比如说小米手机在小米商店中下载应用也是不需要弹出安装对话框的,因为小米可以在MIUI中对Android系统进行各种定制。因此,如果我们只是做一个普通的应用,其实不太需要考虑静默安装这个功能,因为我们只需要将应用上架到相应的商店当中,就会自动拥有静默安装的功能。


    但是如果我们想要做的也是一个类似于商店的平台呢?比如说像360手机助手,它广泛安装于各种各样的手机上,但都是作为一个普通的应用存在的,而没有Google或小米这样的特殊权限,那360手机助手应该怎样做到更好的安装体验呢?为此360手机助手提供了两种方案, 秒装(需ROOT权限)和智能安装,如下图示:




    因此,今天我们就模仿一下360手机助手的实现方式,来给大家提供一套静默安装的解决方案。


    一、秒装


    所谓的秒装其实就是需要ROOT权限的静默安装,其实静默安装的原理很简单,就是调用Android系统的pm install命令就可以了,但关键的问题就在于,pm命令系统是不授予我们权限调用的,因此只能在拥有ROOT权限的手机上去申请权限才行。


    下面我们开始动手,新建一个InstallTest项目,然后创建一个SilentInstall类作为静默安装功能的实现类,代码如下所示:

    /**
     * 静默安装的实现类,调用install()方法执行具体的静默安装逻辑。
     * 原文地址:http://blog.csdn.net/guolin_blog/article/details/47803149
     * @author guolin
     * @since 2015/12/7
     */
    public class SilentInstall {
    
        /**
         * 执行具体的静默安装逻辑,需要手机ROOT。
         * @param apkPath
         *          要安装的apk文件的路径
         * @return 安装成功返回true,安装失败返回false。
         */
        public boolean install(String apkPath) {
            boolean result = false;
            DataOutputStream dataOutputStream = null;
            BufferedReader errorStream = null;
            try {
                // 申请su权限
                Process process = Runtime.getRuntime().exec("su");
                dataOutputStream = new DataOutputStream(process.getOutputStream());
                // 执行pm install命令
                String command = "pm install -r " + apkPath + "\n";
                dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));
                dataOutputStream.flush();
                dataOutputStream.writeBytes("exit\n");
                dataOutputStream.flush();
                process.waitFor();
                errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                String msg = "";
                String line;
                // 读取命令的执行结果
                while ((line = errorStream.readLine()) != null) {
                    msg += line;
                }
                Log.d("TAG", "install msg is " + msg);
                // 如果执行结果中包含Failure字样就认为是安装失败,否则就认为安装成功
                if (!msg.contains("Failure")) {
                    result = true;
                }
            } catch (Exception e) {
                Log.e("TAG", e.getMessage(), e);
            } finally {
                try {
                    if (dataOutputStream != null) {
                        dataOutputStream.close();
                    }
                    if (errorStream != null) {
                        errorStream.close();
                    }
                } catch (IOException e) {
                    Log.e("TAG", e.getMessage(), e);
                }
            }
            return result;
        }
    
    }

    可以看到,SilentInstall类中只有一个install()方法,所有静默安装的逻辑都在这个方法中了,那么我们具体来看一下这个方法。首先在第21行调用了Runtime.getRuntime().exec("su")方法,在这里先申请ROOT权限,不然的话后面的操作都将失败。然后在第24行开始组装静默安装命令,命令的格式就是pm install -r <apk路径>,-r参数表示如果要安装的apk已经存在了就覆盖安装的意思,apk路径是作为方法参数传入的。接下来的几行就是执行上述命令的过程,注意安装这个过程是同步的,因此我们在下面调用了process.waitFor()方法,即安装要多久,我们就要在这里等多久。等待结束之后说明安装过程结束了,接下来我们要去读取安装的结果并进行解析,解析的逻辑也很简单,如果安装结果中包含Failure字样就说明安装失败,反之则说明安装成功。


    整个方法还是非常简单易懂的,下面我们就来搭建调用这个方法的环境。修改activity_main.xml中的代码,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.installtest.MainActivity">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="onChooseApkFile"
                android:text="选择安装包" />
    
            <TextView
                android:id="@+id/apkPathText"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:layout_gravity="center_vertical"
                />
    
        </LinearLayout>
    
    
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@android:color/darker_gray" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onSilentInstall"
            android:text="秒装" />
    
        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@android:color/darker_gray" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onForwardToAccessibility"
            android:text="开启智能安装服务" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onSmartInstall"
            android:text="智能安装" />
    </LinearLayout>

    这里我们先将程序的主界面确定好,主界面上拥有四个按钮,第一个按钮用于选择apk文件的,第二个按钮用于开始秒装,第三个按钮用于开启智能安装服务,第四个按钮用于开始智能安装,这里我们暂时只能用到前两个按钮。那么调用SilentInstall的install()方法需要传入apk路

    径,因此我们需要先把文件选择器的功能实现好,新建activity_file_explorer.xml和list_item.xml作为文件选择器的布局文件,代码分别如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ListView
            android:id="@+id/list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
             />
    
    </LinearLayout>
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="4dp"
        android:orientation="horizontal">
    
        <ImageView android:id="@+id/img"
            android:layout_width="32dp"
            android:layout_margin="4dp"
            android:layout_gravity="center_vertical"
            android:layout_height="32dp"/>
    
    
        <TextView android:id="@+id/name"
            android:textSize="18sp"
            android:textStyle="bold"
            android:layout_width="match_parent"
            android:gravity="center_vertical"
            android:layout_height="50dp"/>
    
    </LinearLayout>
    然后新建FileExplorerActivity作为文件选择器的Activity,代码如下:
    public class FileExplorerActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
    
        ListView listView;
        SimpleAdapter adapter;
        String rootPath = Environment.getExternalStorageDirectory().getPath();
        String currentPath = rootPath;
        List<Map<String, Object>> list = new ArrayList<>();
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_file_explorer);
            listView = (ListView) findViewById(R.id.list_view);
            adapter = new SimpleAdapter(this, list, R.layout.list_item,
                    new String[]{"name", "img"}, new int[]{R.id.name, R.id.img});
            listView.setAdapter(adapter);
            listView.setOnItemClickListener(this);
            refreshListItems(currentPath);
        }
    
        private void refreshListItems(String path) {
            setTitle(path);
            File[] files = new File(path).listFiles();
            list.clear();
            if (files != null) {
                for (File file : files) {
                    Map<String, Object> map = new HashMap<>();
                    if (file.isDirectory()) {
                        map.put("img", R.drawable.directory);
                    } else {
                        map.put("img", R.drawable.file_doc);
                    }
                    map.put("name", file.getName());
                    map.put("currentPath", file.getPath());
                    list.add(map);
                }
            }
            adapter.notifyDataSetChanged();
        }
    
        @Override
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            currentPath = (String) list.get(position).get("currentPath");
            File file = new File(currentPath);
            if (file.isDirectory())
                refreshListItems(currentPath);
            else {
                Intent intent = new Intent();
                intent.putExtra("apk_path", file.getPath());
                setResult(RESULT_OK, intent);
                finish();
            }
    
        }
    
        @Override
        public void onBackPressed() {
            if (rootPath.equals(currentPath)) {
                super.onBackPressed();
            } else {
                File file = new File(currentPath);
                currentPath = file.getParentFile().getPath();
                refreshListItems(currentPath);
            }
        }
    }

    这部分代码由于和我们本篇文件的主旨没什么关系,主要是为了方便demo展示的,因此我就不进行讲解了。


    接下来修改MainActivity中的代码,如下所示:

    /**
     * 仿360手机助手秒装和智能安装功能的主Activity。
     * 原文地址:http://blog.csdn.net/guolin_blog/article/details/47803149
     * @author guolin
     * @since 2015/12/7
     */
    public class MainActivity extends AppCompatActivity {
    
        TextView apkPathText;
    
        String apkPath;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            apkPathText = (TextView) findViewById(R.id.apkPathText);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == 0 && resultCode == RESULT_OK) {
                apkPath = data.getStringExtra("apk_path");
                apkPathText.setText(apkPath);
            }
        }
    
        public void onChooseApkFile(View view) {
            Intent intent = new Intent(this, FileExplorerActivity.class);
            startActivityForResult(intent, 0);
        }
    
        public void onSilentInstall(View view) {
            if (!isRoot()) {
                Toast.makeText(this, "没有ROOT权限,不能使用秒装", Toast.LENGTH_SHORT).show();
                return;
            }
            if (TextUtils.isEmpty(apkPath)) {
                Toast.makeText(this, "请选择安装包!", Toast.LENGTH_SHORT).show();
                return;
            }
            final Button button = (Button) view;
            button.setText("安装中");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    SilentInstall installHelper = new SilentInstall();
                    final boolean result = installHelper.install(apkPath);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (result) {
                                Toast.makeText(MainActivity.this, "安装成功!", Toast.LENGTH_SHORT).show();
                            } else {
                                Toast.makeText(MainActivity.this, "安装失败!", Toast.LENGTH_SHORT).show();
                            }
                            button.setText("秒装");
                        }
                    });
    
                }
            }).start();
    
        }
    
        public void onForwardToAccessibility(View view) {
    
        }
    
        public void onSmartInstall(View view) {
    
        }
    
        /**
         * 判断手机是否拥有Root权限。
         * @return 有root权限返回true,否则返回false。
         */
        public boolean isRoot() {
            boolean bool = false;
            try {
                bool = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bool;
        }
    
    }

    可以看到,在MainActivity中,我们对四个按钮点击事件的回调方法都进行了定义,当点击选择安装包按钮时就会调用onChooseApkFile()方法,当点击秒装按钮时就会调用onSilentInstall()方法。在onChooseApkFile()方法方法中,我们通过Intent打开了FileExplorerActivity,然后在onActivityResult()方法当中读取选择的apk文件路径。在onSilentInstall()方法当中,先判断设备是否ROOT,如果没有ROOT就直接return,然后判断安装包是否已选择,如果没有也直接return。接下来我们开启了一个线程来调用SilentInstall.install()方法,因为安装过程会比较耗时,如果不开线程的话主线程就会被卡住,不管安装成功还是失败,最后都会使用Toast来进行提示。


    代码就这么多,最后我们来配置一下AndroidManifest.xml文件:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.installtest">
    
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <activity android:name=".FileExplorerActivity"/>
        </application>
    
    </manifest>

    并没有什么特殊的地方,由于选择apk文件需要读取SD卡,因此在AndroidManifest.xml文件中要记得声明读SD卡权限。


    另外还有一点需要注意,在Android 6.0系统中,读写SD卡权限被列为了危险权限,因此如果将程序的targetSdkVersion指定成了23则需要做专门的6.0适配,这里简单起见,我把targetSdkVersion指定成了22,因为6.0的适配工作也不在文章的讲解范围之内。


    现在运行程序,就可以来试一试秒装功能了,切记手机一定要ROOT,效果如下图所示:




    可以看到,这里我们选择的网易新闻安装包已成功安装到手机上了,并且没有弹出系统的安装界面,由此证明秒装功能已经成功实现了。


    二、智能安装


    那么对于ROOT过的手机,秒装功能确实可以避免弹出系统安装界面,在不影响用户操作的情况下实现静默安装,但是对于绝大部分没有ROOT的手机,这个功能是不可用的。那么我们应该怎么办呢?为此360手机助手提供了一种折中方案,就是借助Android提供的无障碍服务来实现智能安装。所谓的智能安装其实并不是真正意义上的静默安装,因为它还是要弹出系统安装界面的,只不过可以在安装界面当中释放用户的操作,由智能安装功能来模拟用户点击,安装完成之后自动关闭界面。这个功能是需要用户手动开启的,并且只支持Android 4.1之后的手机,如下图所示:




    好的,那么接下来我们就模仿一下360手机助手,来实现类似的智能安装功能。


    智能安装功能的实现原理要借助Android提供的无障碍服务,关于无障碍服务的详细讲解可参考官方文档:http://developer.android.com/guide/topics/ui/accessibility/services.html


    首先在res/xml目录下新建一个accessibility_service_config.xml文件,代码如下所示:

    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
                           android:packageNames="com.android.packageinstaller"
                           android:description="@string/accessibility_service_description"
                           android:accessibilityEventTypes="typeAllMask"
                           android:accessibilityFlags="flagDefault"
                           android:accessibilityFeedbackType="feedbackGeneric"
                           android:canRetrieveWindowContent="true"
        />

    其中,packageNames指定我们要监听哪个应用程序下的窗口活动,这里写com.android.packageinstaller表示监听Android系统的安装界面。description指定在无障碍服务当中显示给用户看的说明信息,上图中360手机助手的一大段内容就是在这里指定的。accessibilityEventTypes指定我们在监听窗口中可以模拟哪些事件,这里写typeAllMask表示所有的事件都能模拟。accessibilityFlags可以指定无障碍服务的一些附加参数,这里我们传默认值flagDefault就行。accessibilityFeedbackType指定无障碍服务的反馈方式,实际上无障碍服务这个功能是Android提供给一些残疾人士使用的,比如说盲人不方便使用手机,就可以借助无障碍服务配合语音反馈来操作手机,而我们其实是不需要反馈的,因此随便传一个值就可以,这里传入feedbackGeneric。最后canRetrieveWindowContent指定是否允许我们的程序读取窗口中的节点和内容,必须写true。


    记得在string.xml文件中写一下description中指定的内容,如下所示:

    <resources>
        <string name="app_name">InstallTest</string>
        <string name="accessibility_service_description">智能安装服务,无需用户的任何操作就可以自动安装程序。</string>
    </resources>
    
    接下来修改AndroidManifest.xml文件,在里面配置无障碍服务:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.installtest">
    
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            ......
    
            <service
                android:name=".MyAccessibilityService"
                android:label="我的智能安装"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService" />
                </intent-filter>
    
                <meta-data
                    android:name="android.accessibilityservice"
                    android:resource="@xml/accessibility_service_config" />
            </service>
        </application>
    
    </manifest>
    

    这部分配置的内容多数是固定的,必须要声明一个android.permission.BIND_ACCESSIBILITY_SERVICE的权限,且必须要有一个值为android.accessibilityservice.AccessibilityService的action,然后我们通过<meta-data>将刚才创建的配置文件指定进去。


    接下来就是要去实现智能安装功能的具体逻辑了,创建一个MyAccessibilityService类并继承自AccessibilityService,代码如下所示:

    /**
     * 智能安装功能的实现类。
     * 原文地址:http://blog.csdn.net/guolin_blog/article/details/47803149
     * @author guolin
     * @since 2015/12/7
     */
    public class MyAccessibilityService extends AccessibilityService {
    
        Map<Integer, Boolean> handledMap = new HashMap<>();
    
        public MyAccessibilityService() {
        }
    
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            AccessibilityNodeInfo nodeInfo = event.getSource();
            if (nodeInfo != null) {
                int eventType = event.getEventType();
                if (eventType== AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
                        eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
                    if (handledMap.get(event.getWindowId()) == null) {
                        boolean handled = iterateNodesAndHandle(nodeInfo);
                        if (handled) {
                            handledMap.put(event.getWindowId(), true);
                        }
                    }
                }
            }
        }
    
        private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) {
            if (nodeInfo != null) {
                int childCount = nodeInfo.getChildCount();
                if ("android.widget.Button".equals(nodeInfo.getClassName())) {
                    String nodeContent = nodeInfo.getText().toString();
                    Log.d("TAG", "content is " + nodeContent);
                    if ("安装".equals(nodeContent)
                            || "完成".equals(nodeContent)
                            || "确定".equals(nodeContent)) {
                        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                        return true;
                    }
                } else if ("android.widget.ScrollView".equals(nodeInfo.getClassName())) {
                    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                }
                for (int i = 0; i < childCount; i++) {
                    AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
                    if (iterateNodesAndHandle(childNodeInfo)) {
                        return true;
                    }
                }
            }
            return false;
        }
    
        @Override
        public void onInterrupt() {
        }
    
    }
    
    代码并不复杂,我们来解析一下。每当窗口有活动时,就会有消息回调到onAccessibilityEvent()方法中,因此所有的逻辑都是从这里开始的。首先我们可以通过传入的AccessibilityEvent参数来获取当前事件的类型,事件的种类非常多,但是我们只需要监听TYPE_WINDOW_CONTENT_CHANGED和TYPE_WINDOW_STATE_CHANGED这两种事件就可以了,因为在整个安装过程中,这两个事件必定有一个会被触发。当然也有两个同时都被触发的可能,那么为了防止二次处理的情况,这里我们使用了一个Map来过滤掉重复事件。


    接下来就是调用iterateNodesAndHandle()方法来去解析当前界面的节点了,这里我们通过递归的方式将安装界面中所有的子节点全部进行遍历,当发现按钮节点的时候就进行判断,按钮上的文字是不是“安装”、“完成”、“确定”这几种类型,如果是的话就模拟一下点击事件,这样也就相当于帮用户自动操作了这些按钮。另外从Android 4.4系统开始,用户需要将应用申请的所有权限看完才可以点击安装,因此如果我们在节点中发现了ScrollView,那就模拟一下滑动事件,将界面滑动到最底部,这样安装按钮就可以点击了。


    最后,回到MainActivity中,来增加对智能安装功能的调用,如下所示:

    /**
     * 仿360手机助手秒装和智能安装功能的主Activity。
     * 原文地址:http://blog.csdn.net/guolin_blog/article/details/47803149
     * @author guolin
     * @since 2015/12/7
     */
    public class MainActivity extends AppCompatActivity {
    
        ......
    
        public void onForwardToAccessibility(View view) {
            Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
            startActivity(intent);
        }
    
        public void onSmartInstall(View view) {
            if (TextUtils.isEmpty(apkPath)) {
                Toast.makeText(this, "请选择安装包!", Toast.LENGTH_SHORT).show();
                return;
            }
            Uri uri = Uri.fromFile(new File(apkPath));
            Intent localIntent = new Intent(Intent.ACTION_VIEW);
            localIntent.setDataAndType(uri, "application/vnd.android.package-archive");
            startActivity(localIntent);
        }
    
    }

    当点击了开启智能安装服务按钮时,我们通过Intent跳转到系统的无障碍服务界面,在这里启动智能安装服务。当点击了智能安装按钮时,我们通过Intent跳转到系统的安装界面,之后所有的安装操作都会自动完成了。


    现在可以重新运行一下程序,效果如下图所示:




    可以看到,当打开网易新闻的安装界面之后,我们不需要进行任何的手动操作,界面的滑动、安装按钮、完成按钮的点击都是自动完成的,最终会自动回到手机原来的界面状态,这就是仿照360手机助手实现的智能安装功能。


    好的,本篇文章的所有内容就到这里了,虽说不能说完全实现静默安装,但是我们已经在权限允许的范围内尽可能地去完成了,并且360手机助手也只能实现到这一步而已,那些被产品经理逼着去实现静默安装的程序员们也有理由交差了吧?


    源码下载,请点击这里


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢?所谓的静默安装,就是不用弹出系统的安装界面,在不影响用户任何操作的情况下不知不觉地将程序装好。虽说这种方式看上去不打搅用户,但是却存在着...
  • 静默安装和智能安装

    2016-05-25 16:23:49
    android 静默安装(测试没有通过(小米node 1s 已root)现在测试另一部手机魅族node 2)和智能安装可以安装 测试/system/app 中已存在的app也可以智能安装覆盖
  • 所有的静默卸载都必须要root权限,包括网上别人发布的软件,都标明专为有root权限的手机提供 所有的安装卸载都是要通过系统提供的一个确认框 做个检查的功能 如果root就静默 不是root 就只有走系统的安装 ...

    所有的静默卸载都必须要root权限,包括网上别人发布的软件,都标明专为有root权限的手机提供

    所有的安装卸载都是要通过系统提供的一个确认框


    做个检查的功能
    如果root就静默  不是root 就只有走系统的安装


    miui就是有root, 静默也是基于root了的

    展开全文
  • Android 静默安装

    2017-12-27 06:38:36
    遇到了静默安装需求,普通的手机是需要root权限的,root后即可以使用pm命令安装,也可以签名为系统应用后即可使用pm命令静默安装,系统签名命令如下: set apkFile=%1 set signedFile=%apkFile%_signed.apk java -jar ...

    静默安装

    ***First blood *** 遇到了静默安装需求,普通的手机是需要root权限的,root后即可以使用pm命令安装,也可以签名为系统应用后即可使用pm命令静默安装,系统签名命令如下:

    set apkFile=%1
    set signedFile=%apkFile%_signed.apk
    java -jar signapk.jar platform.x509.pem platform.pk8 %apkFile% %signedFile%
    复制代码

    这段命令可直接在windows下存储为bat下次直接把文件拖入即可签名 需要的文件:signapk.jar,platform.x509.pem,platform.pk8 下载链接 注:看手机型号而定,国内的手机型号比较ken...

    在网上找了下资料,记录如下:

    具体实现

    static  DataOutputStream dataOutputStream = null; 
    static BufferedReader errorStream = null; 
    static Process process;
    static boolean result = false;
    public static  boolean slientInstall(String apkPath)
    {  
      try {        
    process = Runtime.getRuntime().exec("su"); //获取su权限
     dataOutputStream = new DataOutputStream(process.getOutputStream());//获取
          String command = "pm install -r "+apkPath+"\n"; //安装命令
     String path = apkPath;       
     Log.e("Tag",command);       
     Log.e("Tag",path+"+++>path");       
         dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));        
         dataOutputStream.flush();     
         dataOutputStream.writeBytes("exit\n");       
         dataOutputStream.flush();       
       try {            
      process.waitFor();           
       errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream())); //记录安装的信息      
     String msg = "";
      String line;           
     while ((line = errorStream.readLine())!= null)
    {               
     msg+=line;          
      }          
      Log.e("Tag","安装的信息是"+msg);   
             if (!msg.contains("Failure"))
                        {
                        return true;           
                         }       
                                                  } 
    catch (InterruptedException e)
     {           
     e.printStackTrace();       
     }   
     }
     catch (IOException e) 
    {        
    e.printStackTrace(); 
       }finally {  
              try {             
       if (dataOutputStream!=null){    
                dataOutputStream.close();   
                 }             
       if (errorStream!= null){      
                  errorStream.close();      
              }        
        } catch (IOException e) {  
                  e.printStackTrace();  
              }   
     }    
    return result;
    }
    复制代码

    上面就实现了静默安装的功能. 记得加上权限:

    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
    复制代码

    ###作者 fuyifang

    转载于:https://juejin.im/post/5a3396d16fb9a0451969a50c

    展开全文
  • 执行静默安装时,需要向手机申请获取root权限,如果手机具有root权限,则可以完成安装。代码如下: MainActivity:
  • Android 静默升级,静默安装

    千次阅读 2017-07-04 17:59:27
    实现静默安装首先手机root权限或者是system 应用  在Android 4.4版本中,静默升级代码如下  // 静默安装,1-安装成功,或没有升级文件,2-升级安装出现异常,-1-程序异常  public static int ...
  • Android 静默安装和静默卸载 功能实现示例代码 实现从2.2到最新4.4.2版本系统 覆盖包括三星 HTC LG 以及中华酷联等绝大多数手机静默安装功能的实现。 有需要改功能的可以下载验证,省去自己的研究时间。
  • android 静默安装

    2017-04-06 12:07:13
    android实际的自动化测试中,应用的整个测试过程中,我们可能都不要人为的手动干预,譬如apk的安装,我们希望它静默安装,不要人手动去确认等。  如果大家看过我第一篇的文章ADB技能使用初级篇(上),相信...
  • Android静默安装Demo

    2014-03-14 13:35:51
    Android静默安装apk的Demo。引入了TrineaAndroidCommon。手机需要root。
  • java实现静默安装apk

    2021-01-05 03:36:13
    静默安装就是偷偷的把一个应用安装到手机上,就是屏蔽确认框,通过反射 只能写个主要的代码,这个是在linux编译用到,因为静默安装需要调用系统服务,在源码下编译,我也是搞了好久 InstallActivity.java package ...
  • 判断手机是否root,我们这篇文章是针对已经root的手机的,root之后获取手机的root权限去实现我们可以想到的手机能做的任意的功能,例如静默安装,卸载软件,手机截图
  • android 静默安装和静默卸载。条件手机必须root。
  • * 静默安装 * @param filePath 文件路径 * @return 安装成功返回true,否则返回false */ public static boolean slientInstall(String filePath) { boolean result = false; Process process = null; ...
  • 如果用过一些手机助手的朋友都知道,手机...本次分享的源码就是一套关于apk静默安装的源码,可以实现apk的静默安装或更新。压缩包里有两种实现apk静默安装的方法说明和详细的操作文档与源码。 涉及模块&技术 静默安装
  • android静默安装与卸载

    2015-03-23 20:36:57
    实现android静默安装,注意:手机需root
  • android静默安装

    2017-02-07 14:05:45
    2.执行静默安装的程序需要申请root权限,代码如下: /** * 应用程序运行命令获取 Root权限,设备必须已破解(获得ROOT权限) * * @return 应用程序是/否获取Root权限 */ public static boolean ...
  • 静默安装apk

    2014-09-22 13:50:08
    下载一个uc.apk放置手机内置sdk里, 把install2.apk放置手机app文件里安装,就完成了,一般很少人提app文件夹
  • 静默安装demo

    2017-02-07 13:58:01
    亲测三星note2手机可运行

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 233
精华内容 93
关键字:

手机静默安装