#android

android中service的简单实现

简单介绍下android中service的用法,其实我也没太研究明白,只是拿出来分享一下。

废话不多说,直接上代码比较好。

一、首先你需要有个类,继承自service。


import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class PushService extends Service {

	public class PushBinder extends Binder {
		public void downLoadFile(final String url) {
			new Thread() {
				@Override
				public void run() {
					super.run();
					System.out.println("开始下载:" + url);
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println("下载完成");
				}
			}.start();
		}
	}

	private final PushBinder binder = new PushBinder();

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		System.out.println("onBind");
		return binder;
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		System.out.println("onCreate");
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		System.out.println("onDestroy");
	}

	@Override
	public void onRebind(Intent intent) {
		// TODO Auto-generated method stub
		super.onRebind(intent);
		System.out.println("onRebind");
	}

	@Override
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		super.onStart(intent, startId);
		System.out.println("onStart:" + startId);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		System.out.println("onUnbind");
		return super.onUnbind(intent);
	}
}```
onCreate:在服务首次创建时调用。

onDestroy:在服务被销毁时调用。

onStart:当使用startService时调用此方法,如果是第一次调用,则会先调用onCreate来创建服务。在服务已经启动的情况下,每次使用startService方法都会调用onStart方法,但服务只会有一个,不会产生多个。(现已改为onStartCommand,onStart不再建议使用)。

onBind:使用bindService时调用此方法,返回一个IBinder,用来与服务进行交互。

onUnBind:使用unbindService时调用。unbindService只能在服务已经绑定状态才会能调用,否则将会抛异常。在一个activity中绑定了service,当activity退出时必须unbindService,否则也会有异常。

 

主要就这几个方法了,这里简单说下service的生命周期。

1、当第一次调用startService或bindService(加入BIND_AUTO_CREATE  flag)时,服务会被创建,onCreate方法被调用。

2、每一次使用startService时onStart方法都会被调用,多次使用bindService,onBind方法只会调用一次。

3、使用stopService会停止服务,onDestroy方法被调用。使用unbindService也会停止服务,onunBind和onDestroy会被调用。但这里有个前提:如果你同时用了startService和bindService,那你必须同时调用unBindService和stopService,服务才会真正被停止。

 

在service中onBind方法返回一个IBinder,用此来与服务进行通信交互。所以服务的一般用法是,在activity中先startServie来启用服务,然后使用bindService来获得IBinder,与服务交互。activity退出时必须调用unBindService,但服务还不会停止,当你不再服务此服务时,调用stopService来停止服务。

 
```package zzp.test.service;

import zzp.test.service.PushService.PushBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class TestServiceActivity extends Activity implements OnClickListener {

	Button start, stop, bind, unbind, download;
	ServiceConnection conn;
	PushBinder push;
	boolean binded = false;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		start = (Button) findViewById(R.id.start);
		stop = (Button) findViewById(R.id.stop);
		bind = (Button) findViewById(R.id.bind);
		unbind = (Button) findViewById(R.id.unbind);
		download = (Button) findViewById(R.id.download);

		start.setOnClickListener(this);
		stop.setOnClickListener(this);
		bind.setOnClickListener(this);
		unbind.setOnClickListener(this);
		download.setOnClickListener(this);
		conn = new ServiceConnection() {

			@Override
			public void onServiceConnected(ComponentName name, IBinder service) {
				// TODO Auto-generated method stub
				System.out.println("onServiceConnected");
				binded = true;
				print(name);
				print(service.getClass().getName());
				push = (PushBinder) service;
			}

			@Override
			public void onServiceDisconnected(ComponentName name) {
				// TODO Auto-generated method stub
				System.out.println("onServiceDisconnected");
				binded = false;
				print(name);
			}

		};
	}

	@Override
	public void onClick(View v) {
		Intent intent = new Intent(this, PushService.class);
		switch (v.getId()) {
		case R.id.bind:
			boolean flag = bindService(intent, conn, Context.BIND_AUTO_CREATE);
			print(flag);
			break;
		case R.id.unbind:
			if (binded) {
				binded = false;
				unbindService(conn);
			}
			break;
		case R.id.start:
			startService(intent);
			break;
		case R.id.stop:
			stopService(intent);
			break;
		case R.id.download:
			push.downLoadFile("http://test.com/icon.png");
			break;
		}
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		if (isFinishing() && binded) {
			unbindService(conn);
			binded = false;
		}
	}

	private void print(Object o) {
		if (o == null) {
			System.out.println("null");
		} else {
			System.out.println(o);
		}
	}
}```
 

另外,你需要在manifest中配置一下服务,这一句话就够了。
```<service android:name="PushService">```
 

其实我还有些疑问,我这里的onServiceDisconnected从来没被调用过,onRebind也从来没被调用过,还不明白为什么,有知道的朋友麻烦指教一下。

 

<span style="color: #ff0000;">注</span>:可能有人遇到过一个类似的异常:

java.lang.ClassCastException: android.os.BinderProxy

在onServiceConnected中对返回的IBinder进行强制类型转换出出现这个异常。解决办法很简单,去掉manifest中service配置中的process属性。使用此属性会将service放到一个单独的线程中,但会出现强制类型转换异常。

读取apk程序包的内容

一、对于已安装应用,只需要getPackageManager().getInstalledPacked(int flags)即可得到PackageInfo.

packageInfo.applicationInto中可以得到所有信息。
注:区别系统应用和用户应用:applicationInfo.flags & ApplicationInfo.FLAG_SYSTEMSTEM
二、对于未安装应用(apk文件)
使用packageManager.getPackageArchiveInfo(String filePath,int flags),只能得到部分信息。
所有int形式的资源(label,icon等)都是无法得到的,需要使用反射机制用到隐藏接口
方法如下:
```Resources res=getResources(); AssetManager asm=new AssetManager();//隐藏api asm.addAssetPath(String apkfilePath);//隐藏api res=new Resources(asm,res.getDisplayMetrics(),res.getConfiguration());//隐藏api```
然后使用res.getString(int resId)   res.getDrawable(int resId)即可得到apk文件内部的资源。(此处资源id可通过上面的公开方法得到)
关键点就在于assetManager.addAssetPath(String apkfilePath)此方法。
现在要做的就是使用反射机制实现上面的隐藏api。
具体反射实现代码如下:
```Class asm_cls = Class.forName("android.content.res.AssetManager"); Object asm_obj = asm_cls.getDeclaredConstructor((Class[]) null).newInstance((Class[]) null); asm_obj.getClass() .getDeclaredMethod("addAssetPath", new Class[] { String.class }) .invoke(asm_obj, new Object[] { filePath }); Resources res=getResources(); res = Resources.class.getDeclaredConstructor( new Class[] { asm_obj.getClass(), res.getDisplayMetrics().getClass(), res.getConfiguration().getClass() }) .newInstance(new Object[] { asm_obj, res.getDisplayMetrics(), res.getConfiguration() }); return res;``` res.getDisplayMetrics(), res.getConfiguration() }); return res;```
res.getString(applicaiontInfo.labelRes);
res.getDrawable(applicationInfo.icon);

ANDROID应用内截图的代码实现

方法一:

View view= getWindow().getDecorView();

Bitmap bmp = Bitmap.createBitmap(480, 800, Bitmap.Config.ARGB_8888);

view.draw(new Canvas(b));

bmp就是截取的图片了,可通过bmp.compress(CompressFormat.PNG, 100, new FileOutputStream(file));把图片保存为文件。

 

方法二:

getWindow().getDecorView().setDrawingCacheEnabled(true);

bmp=getWindow().getDecorView().getDrawingCache();

 

但这样得到的图片是包含状态栏和标题栏的,如果想把状态栏和标题栏去掉,可把得到的图片顶部一部分剪裁掉。

1、得到状态栏高度

Rect rect = new Rect();

view.getWindowVisibleDisplayFrame(rect);

int statusBarHeight = rect.top;

System.out.println("状态栏高度:" + statusBarHeight);ght);ght);

 

2、得到标题栏高度

int wintop = getWindow().findViewById(android.R.id.content).getTop();

int titleBarHeight = wintop - statusBarHeight;

System.out.println("标题栏高度:" + titleBarHeight);ght);ght);

 

 

注:这样得到的截图是不会包含dialog和popupwindow的,你必须单独得到popupwindow的截图,然后再和背景截图合到一起。

另外,截图的相关代码是不能放到oncreate中的,因为这时候getDectorView()得到的是null

 

 

把两个bitmap合到一起的方法很简单。

Bitmap bmpall=Biatmap.createBitmap(width,height,Config.ARGB_8888);

Canvas canvas=new Canvas(bmpall);

canvas.drawBitmap(bmp1,x,y,paint);

canvas.drawBitmap(bmp2,x,y,paint);

得到的bmpall就是合在一起的图片了。

 

ps:按理说也getWindow.findViewById(android.R.id.content)得到的view就是不包含状态栏和标题栏的view,但这个我还没有试过。

监听android的拨号事件(android secret code)

这是个比较冷门的东西,简单的说就是当你在拨号盘输入*##xxxx##*的时候接到通知,然后可以进行相关操作。

首先能想到的应用就是私密类程序。比如你的程序是没有图标的,用户输入这些暗号后可以进行一些操作,或者进入软件界面。

原理很简单,其实也是一个broastcastReceiver而已,只是action并没有放到Intent的常量中,所以很少有人知道。

1 / 2 Next