效果图##

afinal下载文件 android download apk file manager_afinal下载文件 android


DownloadManager(主角)##

作用:下载管理器是一个处理长时间运行的HTTP下载的系统服务。客户端可能要求将URI下载到特定的目标文件。下载管理器将在后台进行下载。

过程:在MainActivity中开启IntentService服务,在IntentService中进行下载操作,完成后发出广播,在MainActivity中接收广播并进行安装apk

两个内部类

class

DownloadManager.Query

用来过滤下载管理器查询

class

DownloadManager.Request

这个类包含请求新下载所必需的所有信息


布局情况

只有一个按钮

afinal下载文件 android download apk file manager_android_02


开启服务

当点击按钮的时候,进行开启服务

//DownloadService继承于IntentService,一会说明
Intent serviceIntent = new Intent(MainActivity.this, DownloadService.class);
//写入你的apk下载地址,下面这个地址只是模拟的
serviceIntent.setData(Uri.parse("http://..../vooloc.apk"));
//开启服务,不要写成了startActivity;
startService(serviceIntent);

新建DownloadService类继承于IntentService##

重写方法onHandleIntent ( Intent intent )

1.Request设定

/*获取下载地址*/
String url = intent.getDataString();
/*获取DownloadManager对象*/
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
/*指定APK缓存路径和应用名称,比如我这个路径就是/storage/emulated/0/Download/vooloc.apk*/
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "vooloc.apk");
/*设置网络下载环境为wifi或者移动数据*/
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
/*设置显示通知栏,下载完成后通知栏自动消失*/
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
/*设置通知栏标题*/
request.setTitle("下载");
/*设置这个下载的描述,显示在通知中*/
request.setDescription("应用正在下载");
/*设置类型为.apk*/
request.setMimeType("application/vnd.android.package-archive");
/*获得唯一下载id*/
long requestId = downloadManager.enqueue(request);

2.Query设定

//将id放进Intent
Intent localIntent = new Intent(BROADCAST_ACTION);
localIntent.putExtra(EXTENDED_DATA_STATUS, requestId);
//查询下载信息
DownloadManager.Query query = new DownloadManager.Query();
//只包括带有给定id的下载。
query.setFilterById(requestId);

3.进行下载

try {
	boolean isGoging = true;
	while (isGoging) {
		Cursor cursor = downloadManager.query(query);
		if (cursor != null && cursor.moveToFirst()) {
		int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
		switch (status) {
			//如果下载状态为成功
			case DownloadManager.STATUS_SUCCESSFUL:
				isGoging = false;
				//调用LocalBroadcastManager.sendBroadcast将intent传递回去
				mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
	            mLocalBroadcastManager.sendBroadcast(localIntent);
				break;
			}
		}
		if (cursor != null) {
			cursor.close();
		}
	}
} catch (Exception e) {
	e.printStackTrace();
}

5.别忘了在清单文件(AndroidManifest)注册服务

<service
	android:name=".DownloadService"
	android:exported="false">
	<intent-filter>
		<action android:name="com.example.android.threadsample.BROADCAST" />
	</intent-filter>
</service>

回到MainActivity,注册广播##

private void regist() {
	IntentFilter intentFilter = new IntentFilter(DownloadService.BROADCAST_ACTION);
	intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
    }

新建类MyReceiver继承于BroadcastReceiver##

/*找到下载好的apk*/
File file=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"vooloc.apk");
/*获取权限*/
try {
	Runtime.getRuntime().exec("chmod 777"+file.getCanonicalPath());
} catch (IOException e) {
	e.printStackTrace();
}
/*由于没有在Activity环境下启动Activity,设置下面的标签*/
intent = new Intent(Intent.ACTION_VIEW);
/*如果设置,这个活动将成为这个历史堆栈上的新任务的开始*/
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*判读版本是否在7.0以上*/
if(Build.VERSION.SDK_INT>=24){
	/*7.0及以上的版本,私有目录被限制访问*/
	Uri apkUri = FileProvider.getUriForFile(context, "com.example.yx.downloadapk.FileProvider", file);
	/*添加这一句表示对目标应用临时授权该Uri所代表的文件*/
	intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
	intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
}else{
	/*7.0以下的版本*/
	intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
startActivity(intent);

取消广播

@Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }

申请权限

Android6.0开始,危险权限需要动态申请!!!

刚好这里的第二个权限是危险权限…

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

动态申请权限

//动态申请权限
    private void requestPermission() {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        }
    }

适配7.0

由于Android7.0以上私有目录被限制访问,还要做些其他工作

打开清单文件(AndroidManifest),加上这段

<provider
	android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.yx.downloadapk.FileProvider"
	android:exported="false"
	android:grantUriPermissions="true">
	<meta-data
		android:name="android.support.FILE_PROVIDER_PATHS"
		android:resource="@xml/file_paths" />
</provider>

第二行的格式 android:authorities=“包名.FileProvider”

接下来在res文件夹下新建xml文件夹

在XML文件夹中新建file_paths.xml

file_paths.xml写入

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

完整代码

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    MyReceiver receiver = new MyReceiver();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //动态申请权限
        requestPermission();
        //初始化数据
        initView();
        //注册广播
        regist();
    }

    //动态申请权限
    private void requestPermission() {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        }
    }

    //初始化数据
    private void initView() {
        Button btn_download = (Button) findViewById(R.id.btn_download);
        btn_download.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //DownloadService继承IntentService,一会说明
        Intent serviceIntent = new Intent(MainActivity.this, DownloadService.class);
        //写入你的apk下载地址,下面这个地址只是模拟的
        serviceIntent.setData(Uri.parse("http://..../vooloc.apk"));
        //开启服务,不要写成了startActivity(serviceIntent);
        startService(serviceIntent);
    }

    public class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "vooloc.apk");
            //获取权限
            try {
                Runtime.getRuntime().exec("chmod 777" + file.getCanonicalPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 由于没有在Activity环境下启动Activity,设置下面的标签
            intent = new Intent(Intent.ACTION_VIEW);
            //如果设置,这个活动将成为这个历史堆栈上的新任务的开始
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //判读版本是否在7.0以上
            if (Build.VERSION.SDK_INT >= 24) {
                //7.0以上的版本
                Uri apkUri = FileProvider.getUriForFile(context, "com.example.yx.downloadapk.FileProvider", file);
                //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            } else {
                //7.0以下的版本
                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            }
            startActivity(intent);
        }
    }

    //注册广播
    private void regist() {
        IntentFilter intentFilter = new IntentFilter(DownloadService.BROADCAST_ACTION);
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }
}

DownloadService

public class DownloadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String BROADCAST_ACTION = "com.example.android.threadsample.BROADCAST";
    public static final String EXTENDED_DATA_STATUS = "com.example.android.threadsample.STATUS";
    private LocalBroadcastManager mLocalBroadcastManager;

    public DownloadService() {
        super("DownloadService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //获取下载地址
        String url = intent.getDataString();
        Log.i(TAG, url);
        //获取DownloadManager对象
        DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        //指定APK缓存路径和应用名称,比如我这个路径就是/storage/emulated/0/Download/vooloc.apk
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "vooloc.apk");
        //设置网络下载环境为wifi或者移动数据
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
        //设置显示通知栏,下载完成后通知栏自动消失
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        //设置通知栏标题
        request.setTitle("下载");
        //设置这个下载的描述,显示在通知中
        request.setDescription("应用正在下载");
        //设置类型为.apk
        request.setMimeType("application/vnd.android.package-archive");
        //获得唯一下载id
        long requestId = downloadManager.enqueue(request);
        //将id放进Intent
        Intent localIntent = new Intent(BROADCAST_ACTION);
        localIntent.putExtra(EXTENDED_DATA_STATUS, requestId);
        //查询下载信息
        DownloadManager.Query query = new DownloadManager.Query();
        //只包括带有给定id的下载。
        query.setFilterById(requestId);
        try {
            boolean isGoging = true;
            while (isGoging) {
                Cursor cursor = downloadManager.query(query);
                if (cursor != null && cursor.moveToFirst()) {
                    int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                    switch (status) {
                        //如果下载状态为成功
                        case DownloadManager.STATUS_SUCCESSFUL:
                            isGoging = false;
                            //调用LocalBroadcastManager.sendBroadcast将intent传递回去
                            mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
                            mLocalBroadcastManager.sendBroadcast(localIntent);
                            break;
                    }
                }
                if (cursor != null) {
                    cursor.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

样例网盘下载

IDE环境不同,直接导入我的项目可能会报红,目的不是去直接使用我的代码,只是为了方便观看,可以在自己的IDE环境下自己写一遍运行看看

下载戳我…下载戳我…下载戳我