Android使用JobService后台实现静默安装apk的功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j086924/article/details/81363776

一.JobService介绍

1.JobService类的继承关系

JobService

extends Service

java.lang.Object
  android.content.Context
     android.content.ContextWrapper
       android.app.Service
        android.app.job.JobService

JobService本质上是一个service,是Google在5.0以后推出的,用来执行一些并非即时执行的后台进程.

2.JobService方法

在JobService中有两个抽象方法onStartJob(JobParameters)和onStopJob(JobParameters)

1).onStartJob()

onStartJob在JobService被调度到的时候会执行,我们只要继承JobService然后重写onStartJob方法,并在里面执行我们的后台任务就可以了,需要注意的是,在onStartJob方法中,执行定时服务主要依靠的是JobScheduler后续项目代码中会用到.

2).onStopJob(): 在此可以实现停止service服务的代码

3).定时启动JobService方法,此处启动在application中进行,保证应用在后台运行时能执行该操作

 private  void scheduleSilentDownloadJob(Context context) {
        
        JobInfo.Builder builder = new JobInfo.Builder(0, new ComponentName(context, DownloadService.class));//此处DownloadService为自定义的Jobservice
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);//设置可需的网络类型
        builder.setPeriodic(24 * 60 * 60 * 1000);//应用在后台的情况下,设置每隔多久启动一次
        builder.setPersisted(true);
        builder.setRequiresCharging(false); // 未充电状态

        JobInfo jobInfo = builder.build();
        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(jobInfo);
    }

4).给JobService添加权限android:permission="android.permission.BIND_JOB_SERVICE"

<service
            android:name="com.jon.download.DownloadService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_JOB_SERVICE" />

 

二.静默安装实现方法

1.一般需要获取root权限才能进行静默安装,此处代码可在android设备自带应用中进行添加

 public static boolean silentInstall(String apkAbsolutePath) {
        boolean isSuccess = false ;
        String[] args = {"pm", "install", "-r", "-d", "-i" , "应用包名","--user","0", apkAbsolutePath};
        ProcessBuilder processBuilder = new ProcessBuilder(args);
        Process process = null;
        InputStream inIs = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            process = processBuilder.start();
            baos.write('/');
            inIs = process.getInputStream();
            byte[] b = new byte[1024];
            while (inIs.read(b) != -1) {
                baos.write(b);
            }
            String res = new String(baos.toByteArray(), "utf-8");
            isSuccess = res.contains("Success");
            baos.close();
        } catch (Exception e) {
            Log.i(TAG,"silentInstall end"+e.toString());
            e.printStackTrace();
        } finally {
            try {
                if (inIs != null) {
                    inIs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (process != null) {
                process.destroy();
            }
        }
        Log.i(TAG,"silentInstall end isSuccess"+isSuccess);
        return isSuccess;
    }

三.部分代码展示: 根据项目需要,实现每天静默安装相关apk,apk路径及相关描述以json的形式保存,需要通过解析,下载,安装完成该功能.

1.BaseJsonUtils.java 

package com.jon.download;

import android.text.TextUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public abstract class BaseJsonUtils<T> {

    private static final int COUNT_EMPTY = 0;


    public List<T> parseJson(String json) throws JSONException {
        JSONArray dataArray = getData(json);
        return parse(dataArray);
    }

    public JSONArray getData(String json) throws JSONException {
        if (TextUtils.isEmpty(json)) {
            return null;
        }
        JSONObject jsonObject = new JSONObject(json);

        JSONArray dataArray = jsonObject.getJSONArray("data");

        if (dataArray.length() <= 0) {
            return null;
        }
        return dataArray;
    }


    private List<T> parse(JSONArray dataArray) throws JSONException {
        List<T> items = new ArrayList<>();
        if (dataArray != null) {
            int length = dataArray.length();

            for (int i = 0; i < length; i++) {
                String jsonString = dataArray.getString(i);
                T item = creatBean(jsonString);
                items.add(item);
            }
        }
        return items;
    }

    public abstract T creatBean(String jsonString) throws JSONException;

    public static String optString(JSONObject jsonObject, String key) {
        if (jsonObject.isNull(key)) {
            return null;
        }
        return jsonObject.optString(key, "");
    }
}

2.CalendarDownloadBean.java

package com.jon.download;

import java.io.Serializable;

public class CalendarDownloadBean implements Serializable {

    private int mID;
    private String mName;
    private String mStar;
    private String mPackageName;
    private String mIconUrl;
    private String mApkUrl;
    private int mSize;
    private String mDeveloper;
    private int mCount;
    private String mVersionName;
    private String mVersionCode;
    private boolean mIsOff;
    private String mIntro;
    private String mDetailUrl;
    private boolean mFee;

    public CalendarDownloadBean() {

    }

    public int getID() {
        return mID;
    }

    public void setID(int mID) {
        this.mID = mID;
    }

    public String getName() {
        return mName;
    }

    public void setName(String mName) {
        this.mName = mName;
    }

    public String getStar() {
        return mStar;
    }

    public void setStar(String mStar) {
        this.mStar = mStar;
    }

    public String getPackageName() {
        return mPackageName;
    }

    public void setPackageName(String mPackageName) {
        this.mPackageName = mPackageName;
    }

    public String getIconUrl() {
        return mIconUrl;
    }

    public void setIconUrl(String mIconUrl) {
        this.mIconUrl = mIconUrl;
    }

    public String getApkUrl() {
        return mApkUrl;
    }

    public void setApkUrl(String mApkUrl) {
        this.mApkUrl = mApkUrl;
    }

    public int getSize() {
        return mSize;
    }

    public void setSize(int mSize) {
        this.mSize = mSize;
    }

    public String getDeveloper() {
        return mDeveloper;
    }

    public void setDeveloper(String mDeveloper) {
        this.mDeveloper = mDeveloper;
    }

    public int getCount() {
        return mCount;
    }

    public void setCount(int mCount) {
        this.mCount = mCount;
    }

    public String getVersionName() {
        return mVersionName;
    }

    public void setVersionName(String mVersionName) {
        this.mVersionName = mVersionName;
    }

    public String getVersionCode() {
        return mVersionCode;
    }

    public void setVersionCode(String mVersionCode) {
        this.mVersionCode = mVersionCode;
    }

    public boolean isIsOff() {
        return mIsOff;
    }

    public void setIsOff(boolean mIsOff) {
        this.mIsOff = mIsOff;
    }

    public String getIntro() {
        return mIntro;
    }

    public void setIntro(String mIntro) {
        this.mIntro = mIntro;
    }

    public String getDetailUrl() {
        return mDetailUrl;
    }

    public void setDetailUrl(String mDetailUrl) {
        this.mDetailUrl = mDetailUrl;
    }

    public boolean isFee() {
        return mFee;
    }

    public void setFee(boolean mFee) {
        this.mFee = mFee;
    }
}

3.DownloadParserUtils.java

package com.jon.download;

import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;
import com.jon.download.CalendarDownloadBean;
import com.jon.download.BaseJsonUtils;

public class DownloadParserUtils extends BaseJsonUtils<CalendarDownloadBean> {

    private static DownloadParserUtils sInstance;

    private DownloadParserUtils(){}

    public static DownloadParserUtils getInstance() {
        if (sInstance == null) {
            synchronized (DownloadParserUtils.class) {
                if (sInstance == null) {
                    sInstance = new DownloadParserUtils();
                }
            }
        }
        return sInstance;
    }

    @Override
    public CalendarDownloadBean creatBean(String jsonString) throws JSONException {
        JSONObject jsonObject = new JSONObject(jsonString);
        CalendarDownloadBean item = new CalendarDownloadBean();
        item.setID(jsonObject.optInt("id"));
        item.setName(jsonObject.getString("name"));
        item.setStar(jsonObject.getString("star"));
        item.setPackageName(jsonObject.getString("packageName"));
        item.setIconUrl(jsonObject.getString("iconUrl"));
        item.setApkUrl(jsonObject.getString("apkUrl"));
        item.setSize(jsonObject.optInt("size"));
        item.setDeveloper(jsonObject.getString("developer"));
        item.setCount(jsonObject.optInt("count"));
        item.setVersionName(jsonObject.getString("versionName"));
        item.setVersionCode(jsonObject.getString("versionCode"));
        item.setIsOff(jsonObject.optInt("isOff")==1);
        item.setIntro(jsonObject.getString("intro"));
        item.setDetailUrl(jsonObject.getString("detailUrl"));
        item.setFee(jsonObject.optInt("fee")==0);
        return item;
    }

}

4.DownloadService.java

package com.jon.download;

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.jon.download.CalendarDownloadBean;
import com.jon.download.DownloadUtils;
import com.jon.download.DownloadParserUtils;
import org.json.JSONArray;
import org.json.JSONException;


public class DownloadService extends JobService {

    private static final String TAG = "DownloadService";
    private static final String TEST_URL = "";
    private static final String REAL_URL = "";
    public DownloadService() {
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.i(TAG,"onStartJob-----------");
        final int id = jobParameters.getJobId();
        initDownloadData(this);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        return false;
    }


    public  void initDownloadData(final Context context){

        String jsonStr=getServiceUrl();
        Log.i(TAG,jsonStr.toString());

        try {
            DownloadUtils.readParse(jsonStr, new DownloadUtils.CallBack() {
                List<CalendarDownloadBean> mList=new ArrayList<CalendarDownloadBean>();
                @Override
                public void successful(String json) {
                    Log.i(TAG,json.toString());
                    try {
                        if(mList.size()>0){
                            mList.clear();
                        }
                        mList=filterList(context,json);
                        Log.i(TAG,"mList.size()="+mList.size());

                        if(mList.size()>0&&mList!=null){
                            new DownloadFileAsync(context,mList).execute();
                        }

                    }catch (JSONException e){
                        Log.i(TAG,"JSONException"+e.toString());
                    }
                }

                @Override
                public void error(Exception exception) {
                    Log.i(TAG,exception.toString()+"");
                }
            });
        }catch (Exception e){
            Log.i(TAG ,"Exception"+e.toString()+"");

        }
    }

    private List<CalendarDownloadBean> filterList(Context context,String json) throws JSONException{
        List<CalendarDownloadBean> mList=new ArrayList<CalendarDownloadBean>();
        mList= DownloadParserUtils.getInstance().parseJson(json);
        if(mList.size()>0&&mList!=null){
            Iterator<CalendarDownloadBean> iterator=mList.iterator();
            while(iterator.hasNext()){
                CalendarDownloadBean bean=(CalendarDownloadBean)iterator.next();
                String packageName=bean.getPackageName();
                String newVersionCode=bean.getVersionCode();
                if(DownloadUtils.checkApkIsInstall(context,packageName)){
                    String oldVersionCode=DownloadUtils.getPackageVersionCode(context,packageName)+"";
                    Log.i(TAG,"newVersionCode=="+newVersionCode+"--oldVersionCode"+oldVersionCode);
                    if(DownloadUtils.compareVersion(newVersionCode,oldVersionCode)!=1){
                        iterator.remove();
                    }
                }else{
                    String prefInstallDate=DownloadUtils.getPrefPackageInstallDate(context,packageName);
                    String nowDate=DownloadUtils.getCalendarDate();
                    Log.i(TAG,"nowDate=="+nowDate+"--prefInstallDate="+prefInstallDate);
                    if(null!=prefInstallDate&&!prefInstallDate.equals("")){
                        if(DownloadUtils.compareVersion(nowDate,prefInstallDate)==0){
                            iterator.remove();
                        }
                    }
                }
            }//while
        }//if
        return mList;
    }

    private static String getServiceUrl() {
        StringBuffer buffer = new StringBuffer();
        buffer.append(Utils.isTestEnvironment() ? TEST_URL : REAL_URL);
        //buffer.append(TEST_URL);
        buffer.append("/jon/app/recom.do?chan=");
        buffer.append("&rows=10");
        return buffer.toString();
    }

}

5.DownloadFileAsync.java

package com.jon.download;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jon.download.CalendarDownloadBean;
import com.jon.download.DownloadUtils;


public class DownloadFileAsync extends AsyncTask<String,String ,String> {

    private static final String TAG="DownloadFileAsync";
    private  String apkUrl="";
    private String[] fileStrings={};
    private static final int BUFFER_SIZE = 10 * 1024; // 8k ~ 32K
    private Context mContext;
    private List<CalendarDownloadBean> mList;

    public DownloadFileAsync(Context context,List<CalendarDownloadBean> mBeanList) {
        Log.i(TAG,"mBeanList.size()="+mBeanList.size());
        mList=new ArrayList<CalendarDownloadBean>();
        this.mContext=context;
        this.mList=mBeanList;
        fileStrings=new String[mBeanList.size()];
    }


    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }


    @Override
    protected String doInBackground(String... strings) {
        if(mList!=null){
            for (int i=0;i<mList.size();i++){
                apkUrl=mList.get(i).getApkUrl();
                Log.i(TAG,"doInBackground==strings[i]="+i+" "+apkUrl);
                InputStream in = null;
                FileOutputStream out = null;
                File apkFile =null;
                try {
                    URL url = new URL(apkUrl);
                    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

                    urlConnection.setRequestMethod("GET");
                    urlConnection.setDoOutput(false);
                    urlConnection.setConnectTimeout(10 * 1000);
                    urlConnection.setReadTimeout(10 * 1000);
                    urlConnection.setRequestProperty("Connection", "Keep-Alive");
                    urlConnection.setRequestProperty("Charset", "UTF-8");
                    urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");

                    urlConnection.connect();
                    int byteread = 0;
                    in = urlConnection.getInputStream();
                    File dir = DownloadUtils.createApkDir(mContext);
                    String apkName = apkUrl.substring(apkUrl.lastIndexOf("/") + 1, apkUrl.length());
                    apkFile = new File(dir.getPath(),apkName);
                    if(!apkFile.exists()){
                        apkFile.createNewFile();
                    }
                    out = new FileOutputStream(apkFile);
                    byte[] buffer = new byte[BUFFER_SIZE];
                    while ((byteread = in.read(buffer)) != -1) {
                        out.write(buffer, 0, byteread);
                    }
                    // 下载完成
                    Log.i(TAG,"downloadApk------ok=-----");
                } catch (Exception e) {
                    Log.e(TAG, "download apk file error:" + e.getMessage());
                } finally {
                    if (out != null) {
                        try {
                            out.close();
                        } catch (IOException e) {

                        }
                    }
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {

                        }
                    }
                }
                if(null!=apkFile){
                    fileStrings[i]=apkFile.exists()?apkFile.toString():null;
                }
                //return apkFile.exists()?apkFile.toString():null;
            }//for
        }
        return null;
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        for (int i=0;i<fileStrings.length;i++){
            Log.i(TAG,"onPostExecute---"+fileStrings[i].toString());
            //if(!DownloadUtils.checkApkIsInstall(mContext,packageNames[i])){
            if(null!=fileStrings[i]){
                final File file=new File(fileStrings[i]);
                final CalendarDownloadBean bean=(CalendarDownloadBean)mList.get(i);
                if(file.exists()){
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            if(DownloadUtils.silentInstall(file.toString())){
                                DownloadUtils.putPrefPackageInstallDate(mContext,bean.getPackageName(),DownloadUtils.getCalendarDate());
                                if(file.exists()){
                                    file.delete();
                                }
                            }
                        }
                    }).start();
                }
            }//if
        }//for

    }

}

6.DownloadUtils.java

package com.jon.download;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.v4.content.FileProvider;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;
import android.content.pm.PackageInfo;
import java.util.Calendar;


public class DownloadUtils {

    private static final String TAG = "ApkUtils";
    private static final String APK_PATH="/jon/download/";
    private static final String SILENT_DOWNLOAD_PREFS="silent_download_prefs";

    public static File createApkDir(Context context){
        String apkDir = null;
        if(isExistSdcard()){
            apkDir =  Environment.getExternalStorageDirectory() + APK_PATH;// 保存到SD卡路径下
        }else{
            apkDir = context.getFilesDir().getAbsolutePath() + APK_PATH;// 保存到app的包名路径下
        }
        File destDir = new File(apkDir);
        if (!destDir.exists()) {// 判断文件夹是否存在
            destDir.mkdirs();
        }
        return  destDir;
    }

    private static boolean isExistSdcard(){
        String status = Environment.getExternalStorageState();
        if (status.equals(Environment.MEDIA_MOUNTED)) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean checkApkIsInstall(Context context, String packageName) {

        if (packageName == null || "".equals(packageName)) {
            return false;
        }
        try {
            ApplicationInfo info = context.getPackageManager()
                    .getApplicationInfo(packageName,
                            PackageManager.GET_UNINSTALLED_PACKAGES);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    public static boolean silentInstall(String apkAbsolutePath) {
        boolean isSuccess = false ;
        String[] args = {"pm", "install", "-r", "-d", "-i" , "应用包名","--user","0", apkAbsolutePath};
        ProcessBuilder processBuilder = new ProcessBuilder(args);
        Process process = null;
        InputStream inIs = null;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            process = processBuilder.start();
            baos.write('/');
            inIs = process.getInputStream();
            byte[] b = new byte[1024];
            while (inIs.read(b) != -1) {
                baos.write(b);
            }
            String res = new String(baos.toByteArray(), "utf-8");
            isSuccess = res.contains("Success");
            baos.close();
        } catch (Exception e) {
            Log.i(TAG,"silentInstall end"+e.toString());
            e.printStackTrace();
        } finally {
            try {
                if (inIs != null) {
                    inIs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (process != null) {
                process.destroy();
            }
        }
        Log.i(TAG,"silentInstall end isSuccess"+isSuccess);
        return isSuccess;
    }

    public interface CallBack {
        void successful(String e);
        void error(Exception exception);

    }

    public static void readParse(final String urlPath, final CallBack callBack) throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                URL url=null;
                HttpURLConnection conn=null;
                InputStream inStream=null;
                ByteArrayOutputStream outStream=null;
                try{
                    outStream = new ByteArrayOutputStream();
                    byte[] data = new byte[1024];
                    int len = 0;
                    url = new URL(urlPath);
                    conn= (HttpURLConnection) url.openConnection();
                    inStream = conn.getInputStream();
                    while ((len = inStream.read(data)) != -1) {
                        outStream.write(data, 0, len);
                    }
                    callBack.successful(new String(outStream.toByteArray()));
                }catch (Exception e){
                    callBack.error(e);
                }finally {
                    if (null!=outStream){
                        try {
                            outStream.close();
                        }catch (IOException e){

                        }
                    }

                    if (null!=inStream){
                        try {
                            inStream.close();
                        }catch (IOException e){

                        }
                    }

                    if (null!=conn){
                        conn.disconnect();
                    }
                }
            }
        }).start();
    }

    public static int getPackageVersionCode(Context context, String packageName) {
        //上下文,包名
        PackageManager pm = context.getPackageManager();
        PackageInfo pi = null;
        try {
            pi = pm.getPackageInfo(packageName,
                    PackageManager.GET_CONFIGURATIONS);
            return pi.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     *
     * @param version1
     * @param version2
     * 0代表相等,1代表version1大于version2,-1代表version1小于version2
     * @return
     */
    public static int compareVersion(String version1, String version2) {
        if (version1.equals(version2)) {
            return 0;
        }
        String[] version1Array = version1.split("\\.");
        String[] version2Array = version2.split("\\.");
        int index = 0; // 获取最小长度值
        int minLen = Math.min(version1Array.length, version2Array.length);
        int diff = 0; // 循环判断每位的大小
        while (index < minLen && (diff = Integer.parseInt(version1Array[index]) - Integer.parseInt(version2Array[index])) == 0) {
            index++;
        }if (diff == 0){ // 如果位数不一致,比较多余位数
            for (int i = index; i < version1Array.length; i++){
                if (Integer.parseInt(version1Array[i]) > 0) {
                    return 1;
                }
            }

            for (int i = index; i < version2Array.length; i++) {
                if (Integer.parseInt(version2Array[i]) > 0) {
                    return -1;
                }
            }

            return 0;
        } else {
            return diff > 0 ? 1 : -1;
        }
    }

    public static SharedPreferences getSharedPreferences(Context context) {
        SharedPreferences sharedPreferences = context.getSharedPreferences(SILENT_DOWNLOAD_PREFS,
                Context.MODE_PRIVATE);
        return sharedPreferences;
    }

    public static void putPrefPackageInstallDate(Context context,String packageName,String date){
        SharedPreferences sharedPreferences = getSharedPreferences(context);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(packageName,date);
        editor.apply();
    }

    public static String  getPrefPackageInstallDate(Context context,String packageName){
        SharedPreferences sharedPreferences = getSharedPreferences(context);
        String versionCode=sharedPreferences.getString(packageName,"");
        return versionCode;
    }

    public static String getCalendarDate(){
        Calendar calendar = Calendar.getInstance();
        StringBuilder sb=new StringBuilder();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH)+1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        sb.append(year).append(month).append(day);
        Log.i(TAG,"getCalendarDay "+sb.toString());
        return sb.toString();
    }

}

猜你喜欢

转载自blog.csdn.net/j086924/article/details/81363776
今日推荐