本文介绍两种提取完整apk(系统预装的和用户安装的)
一、通过adb提取(适合开发者)
1. adb shell pm list packages找到要提取apk的包名
F:\winadb>adb shell pm list packages
package:com.android.fmradio
package:com.mediatek.gba
package:com.mediatek.ims
package:com.android.cts.priv.ctsshim
package:com.google.android.youtube
package:org.simalliance.openmobileapi.uicc2terminal
package:com.google.android.ext.services
package:com.android.providers.telephony
...
2. adb shell pm path 定位apk所在系统路径
F:\winadb>adb shell pm path com.google.android.youtube
package:/system/app/YouTube/YouTube.apk
3. adb pull
F:\winadb>adb pull /system/app/YouTube/YouTube.apk
9745 KB/s (31047955 bytes in 3.111s)
二、app实现提取(适合大众)
比如坐高铁(或火车),看见隔壁的小伙伴玩的某款游戏,好像挺有意思,也想玩一下,好不容易向人家问到了,App名字,但是高速奔跑的高铁上没有WIFI,4G/3G网络(2G就不考虑了)又不好,怎么下载呢?如果能离线传输岂不更好?的确有,比如蓝牙、WIFI P2P(WIFI直连)都可以传输文件。这种情况还得apk在手机存储卡,否则也无法分享了。
下面就介绍通过app的方式将app(系统预装的和用户安装的)提取到手机存储(sdcard),实现的原理就像shell命令cp,java的实现是通过文件输入/输出流进行拷贝。(思路借鉴快牙app)
先上一张图,左边是快牙app,右边是本文说的demo
demo上面三个按钮用于过滤显示系统应用和用户自己安装的应用,每个item显示应用的图标、应用的名称、应用的包名、应用所在系统路径,一一对应,很容找到想要备份的apk了,长按弹出ContextMenu即可备份,代码比较简单,就一个类,直接贴出来了
package com.android.backupapp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.android.backupapp.R.string;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements
OnCreateContextMenuListener, OnClickListener {
private PackageManager mPackageManager;
private Button mBtnAllApps;
private Button mBtnSystemApps;
private Button mBtnDataApps;
private ListView mListView;
private AppListAdapter mAdapter;
public int MID;
private static final String BACKUP_PATH = "/sdcard/Backup/";
private static final String APK = ".apk";
private static final String ODEX = ".odex";
private static final String OAT32 = "oat/arm";
private static final String OAT64 = "oat/arm64";
public static enum AppType {
SYSTEM, DATA, ALL
}
private static final int EVENT_COMPLETE = 1;
private Handler mH = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_COMPLETE:
Toast.makeText(MainActivity.this, (String) msg.obj,
Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.listview);
mListView.setOnCreateContextMenuListener(this);
mBtnAllApps = (Button) findViewById(R.id.all_apps);
mBtnSystemApps = (Button) findViewById(R.id.system_apps);
mBtnDataApps = (Button) findViewById(R.id.data_apps);
mBtnAllApps.setOnClickListener(this);
mBtnSystemApps.setOnClickListener(this);
mBtnDataApps.setOnClickListener(this);
mPackageManager = getPackageManager();
List lists = mPackageManager
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES);
Collections.sort(lists, new DisplayNameComparator(mPackageManager));
mAdapter = new AppListAdapter(this, lists);
mListView.setAdapter(mAdapter);
}
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
if (view == mBtnAllApps) {
if (mAdapter.getAppType() != AppType.ALL) {
mAdapter.setAppType(AppType.ALL);
mAdapter.notifyDataSetChanged();
}
} else if (view == mBtnSystemApps) {
if (mAdapter.getAppType() != AppType.SYSTEM) {
mAdapter.setAppType(AppType.SYSTEM);
mAdapter.notifyDataSetChanged();
}
} else if (view == mBtnDataApps) {
if (mAdapter.getAppType() != AppType.DATA) {
mAdapter.setAppType(AppType.DATA);
mAdapter.notifyDataSetChanged();
}
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
MenuInflater inflater = new MenuInflater(this);
inflater.inflate(R.menu.context_menu, menu);
super.onCreateContextMenu(menu, v, menuInfo);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// TODO Auto-generated method stub
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
MID = (int) info.id;
PackageInfo pi = mAdapter.getItem(MID);
switch (item.getItemId()) {
case R.id.backup:
backupApp(pi);
return true;
}
return super.onContextItemSelected(item);
}
private Drawable getAppIcon(PackageInfo packageInfo) {
return packageInfo.applicationInfo.loadIcon(mPackageManager);
}
private String getAppName(PackageInfo packageInfo) {
return packageInfo.applicationInfo.loadLabel(mPackageManager)
.toString();
}
private String getAppPackageName(PackageInfo packageInfo) {
return packageInfo.packageName;
}
private String getAppSourceDir(PackageInfo packageInfo) {
return packageInfo.applicationInfo.sourceDir;
}
private void backupApp(PackageInfo packageInfo) {
String source = getAppSourceDir(packageInfo);
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
String key = getAppName(packageInfo);
String dest = BACKUP_PATH + key + APK;
Toast.makeText(this, R.string.backup_start, Toast.LENGTH_SHORT)
.show();
new Thread(new CopyRunnable(source, dest, key)).start();
} else {
String path = new File(source).getParent();
String name = new File(source).getName();
name = name.substring(0, name.indexOf(APK));
String kitKat = new File(path).getParent() + File.separator + name
+ ODEX;
String oat32 = path + File.separator + OAT32 + File.separator
+ name + ODEX;
String oat64 = path + File.separator + OAT64 + File.separator
+ name + ODEX;
if (!new File(kitKat).exists() && !new File(oat32).exists()
&& !new File(oat64).exists()) {
String key = getAppName(packageInfo);
if (getAppIcon(packageInfo).equals(
getAppPackageName(packageInfo))) {
key = name;
}
String dest = BACKUP_PATH + key + APK;
Toast.makeText(this, R.string.backup_start, Toast.LENGTH_SHORT)
.show();
new Thread(new CopyRunnable(source, dest, key)).start();
} else {
Toast.makeText(this, R.string.apk_has_oat, Toast.LENGTH_SHORT)
.show();
}
}
}
private class CopyRunnable implements Runnable {
private String source;
private String dest;
private String key;
public CopyRunnable(String source, String dest, String key) {
this.source = source;
this.dest = dest;
this.key = key;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
int length = 1024 * 1024;
if (!new File(BACKUP_PATH).exists()) {
new File(BACKUP_PATH).mkdirs();
}
File fDest = new File(dest);
if (fDest.exists()) {
fDest.delete();
}
fDest.createNewFile();
FileInputStream in = new FileInputStream(new File(source));
FileOutputStream out = new FileOutputStream(fDest);
FileChannel inC = in.getChannel();
FileChannel outC = out.getChannel();
int i = 0;
while (true) {
if (inC.position() == inC.size()) {
inC.close();
outC.close();
Message message = mH.obtainMessage(EVENT_COMPLETE);
message.obj = MainActivity.this.getString(
R.string.backup_success, key, dest);
mH.sendMessage(message);
break;
}
if ((inC.size() - inC.position()) < 1024 * 1024) {
length = (int) (inC.size() - inC.position());
} else {
length = 1024 * 1024;
}
inC.transferTo(inC.position(), length, outC);
inC.position(inC.position() + length);
i++;
}
} catch (Exception e) {
// TODO: handle exception
Log.e("zhouyj", e.toString());
}
}
}
private class AppListAdapter extends BaseAdapter {
private Context context;
private List lists;
private AppType appType = AppType.DATA;
public AppListAdapter(Context context, List lists) {
this.context = context;
this.lists = lists;
}
public AppType getAppType() {
return appType;
}
public void setAppType(AppType appType) {
this.appType = appType;
}
private List getDataList(AppType appType) {
List l = new ArrayList();
switch (appType) {
case SYSTEM:
for (PackageInfo pi : lists) {
if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
l.add(pi);
}
}
break;
case DATA:
for (PackageInfo pi : lists) {
if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
l.add(pi);
}
}
break;
case ALL:
return lists;
}
return l;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return getDataList(getAppType()).size();
}
@Override
public PackageInfo getItem(int position) {
// TODO Auto-generated method stub
return getDataList(getAppType()).get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
PackageInfo pi = getItem(position);
ViewHolder viewHolder = null;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater mInflater = LayoutInflater.from(context);
convertView = mInflater.inflate(R.layout.layout_item, null);
viewHolder.imageView = (ImageView) convertView
.findViewById(R.id.drawable);
viewHolder.tvAppName = (TextView) convertView
.findViewById(R.id.app_name);
viewHolder.tvPackageName = (TextView) convertView
.findViewById(R.id.package_name);
viewHolder.tvPath = (TextView) convertView
.findViewById(R.id.app_path);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setImageDrawable(getAppIcon(pi));
viewHolder.tvAppName.setText(getAppName(pi));
viewHolder.tvPackageName.setText(getAppPackageName(pi));
viewHolder.tvPath.setText(getAppSourceDir(pi));
return convertView;
}
}
private static class ViewHolder {
ImageView imageView;
TextView tvAppName;
TextView tvPackageName;
TextView tvPath;
}
public static class DisplayNameComparator implements
Comparator {
public DisplayNameComparator(PackageManager pm) {
mPM = pm;
mCollator.setStrength(Collator.PRIMARY);
}
public final int compare(PackageInfo a, PackageInfo b) {
// We want to put the one targeted to another user at the end of the
// dialog.
CharSequence sa = a.applicationInfo.loadLabel(mPM);
if (sa == null)
sa = a.packageName;
CharSequence sb = b.applicationInfo.loadLabel(mPM);
if (sb == null)
sb = b.packageName;
return mCollator.compare(sa.toString(), sb.toString());
}
private final Collator mCollator = Collator.getInstance();
private PackageManager mPM;
}
}