标签: Android

Android 监听应用程序安装和卸载

*、             新建监听类:BootReceiver继承BroadcastReceiver

publicclass BootReceiver extends BroadcastReceiver {
@Override

public void onReceive(Context context,Intent intent) {
//接收广播:系统启动完成后运行程序

if(intent.getAction().equals(“android.intent.action.BOOT_COMPLETED”)) {
Intent newIntent = new Intent(context,WatchInstall.class);

newIntent.setAction(“android.intent.action.MAIN”);             newIntent.addCategory(“android.intent.category.LAUNCHER”);           newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            context.startActivity(newIntent);

}

//接收广播:设备上新安装了一个应用程序包后自动启动新安装应用程序。

if(intent.getAction().equals(“android.intent.action.PACKAGE_ADDED”)) {
String packageName =intent.getDataString().substring(8);

System.out.println(“—————” + packageName);

Intent newIntent = new Intent();

newIntent.setClassName(packageName,packageName+ .MainActivity”);

newIntent.setAction(“android.intent.action.MAIN”);            newIntent.addCategory(“android.intent.category.LAUNCHER”);             newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

context.startActivity(newIntent);

}

//接收广播:设备上删除了一个应用程序包。

if(intent.getAction().equals(“android.intent.action.PACKAGE_REMOVED”)){
System.out.println(“********************************”);

}

}

第二、             修改AndroidManifest.xml配置文件

<?xmlversion=”1.0″ encoding=”UTF-8″?>

<manifestxmlns:android=”http://schemas.android.com/apk/res/android”

package=”org.me.watchinstall”>

<application>

<receiverandroid:name=”.BootReceiver”

android:label=”@string/app_name”>

<intent-filter>

<actionandroid:name=”android.intent.action.BOOT_COMPLETED”/>

<categoryandroid:name=”android.intent.category.LAUNCHER” />

</intent-filter>

<intent-filter>

<actionandroid:name=”android.intent.action.PACKAGE_ADDED” />

<actionandroid:name=”android.intent.action.PACKAGE_REMOVED” />

<data android:scheme=”package” />

<!– 注意!!这句必须要加,否则接收不到BroadCast –>

</intent-filter>

</receiver>

<activityandroid:name=”.WatchInstall”android:label=”WatchInstall”>

<intent-filter>

<actionandroid:name=”android.intent.action.MAIN”/>

<categoryandroid:name=”android.intent.category.LAUNCHER”/>

</intent-filter>

</activity>

</application>

</manifest>

Android 异步加载网络图片

Android图片的异步加载,主要原理:

加载图片时先查看缓存中时候存在该图片,如果存在则返回该图片,否则先加载载一个默认的占位图片,同时创建一个通过网络获取图片的任务并添加,任务完成后放松消息给主线程更新界面。

使用方法:

[java]  view plain copy
AsynImageLoader asynImageLoader = new AsynImageLoader();
asynImageLoader.showImageAsyn(imageView, imageUrl, resId);

 

 

类代码:

[java]  view plain copy
package com.wangge.uumao.http;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ImageView;

import com.wangge.uumao.util.PicUtil;

public class AsynImageLoader {
private static final String TAG = “AsynImageLoader”;
// 缓存下载过的图片的Map
private Map<String, SoftReference<Bitmap>> caches;
// 任务队列
private List<Task> taskQueue;
private boolean isRunning = false;

public AsynImageLoader(){
// 初始化变量
caches = new HashMap<String, SoftReference<Bitmap>>();
taskQueue = new ArrayList<AsynImageLoader.Task>();
// 启动图片下载线程
isRunning = true;
new Thread(runnable).start();
}

/**
*
* @param imageView 需要延迟加载图片的对象
* @param url 图片的URL地址
* @param resId 图片加载过程中显示的图片资源
*/
public void showImageAsyn(ImageView imageView, String url, int resId){
imageView.setTag(url);
Bitmap bitmap = loadImageAsyn(url, getImageCallback(imageView, resId));

if(bitmap == null){
imageView.setImageResource(resId);
}else{
imageView.setImageBitmap(bitmap);
}
}

public Bitmap loadImageAsyn(String path, ImageCallback callback){
// 判断缓存中是否已经存在该图片
if(caches.containsKey(path)){
// 取出软引用
SoftReference<Bitmap> rf = caches.get(path);
// 通过软引用,获取图片
Bitmap bitmap = rf.get();
// 如果该图片已经被释放,则将该path对应的键从Map中移除掉
if(bitmap == null){
caches.remove(path);
}else{
// 如果图片未被释放,直接返回该图片
Log.i(TAG, “return image in cache” + path);
return bitmap;
}
}else{
// 如果缓存中不常在该图片,则创建图片下载任务
Task task = new Task();
task.path = path;
task.callback = callback;
Log.i(TAG, “new Task ,” + path);
if(!taskQueue.contains(task)){
taskQueue.add(task);
// 唤醒任务下载队列
synchronized (runnable) {
runnable.notify();
}
}
}

// 缓存中没有图片则返回null
return null;
}

/**
*
* @param imageView
* @param resId 图片加载完成前显示的图片资源ID
* @return
*/
private ImageCallback getImageCallback(final ImageView imageView, final int resId){
return new ImageCallback() {

@Override
public void loadImage(String path, Bitmap bitmap) {
if(path.equals(imageView.getTag().toString())){
imageView.setImageBitmap(bitmap);
}else{
imageView.setImageResource(resId);
}
}
};
}

private Handler handler = new Handler(){

@Override
public void handleMessage(Message msg) {
// 子线程中返回的下载完成的任务
Task task = (Task)msg.obj;
// 调用callback对象的loadImage方法,并将图片路径和图片回传给adapter
task.callback.loadImage(task.path, task.bitmap);
}

};

private Runnable runnable = new Runnable() {

@Override
public void run() {
while(isRunning){
// 当队列中还有未处理的任务时,执行下载任务
while(taskQueue.size() > 0){
// 获取*个任务,并将之从任务队列中删除
Task task = taskQueue.remove(0);
// 将下载的图片添加到缓存
task.bitmap = PicUtil.getbitmap(task.path);
caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
if(handler != null){
// 创建消息对象,并将完成的任务添加到消息对象中
Message msg = handler.obtainMessage();
msg.obj = task;
// 发送消息回主线程
handler.sendMessage(msg);
}
}

//如果队列为空,则令线程等待
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};

//回调接口
public interface ImageCallback{
void loadImage(String path, Bitmap bitmap);
}

class Task{
// 下载任务的下载路径
String path;
// 下载的图片
Bitmap bitmap;
// 回调对象
ImageCallback callback;

@Override
public boolean equals(Object o) {
Task task = (Task)o;
return task.path.equals(path);
}
}
}

*后附上PicUtil类的代码,之前忘了贴这个类的代码,不好意识了~~

[java]  view plain copy
public class PicUtil {
private static final String TAG = “PicUtil”;

/**
* 根据一个网络连接(URL)获取bitmapDrawable图像
*
* @param imageUri
* @return
*/
public static BitmapDrawable getfriendicon(URL imageUri) {

BitmapDrawable icon = null;
try {
HttpURLConnection hp = (HttpURLConnection) imageUri
.openConnection();
icon = new BitmapDrawable(hp.getInputStream());// 将输入流转换成bitmap
hp.disconnect();// 关闭连接
} catch (Exception e) {
}
return icon;
}

/**
* 根据一个网络连接(String)获取bitmapDrawable图像
*
* @param imageUri
* @return
*/
public static BitmapDrawable getcontentPic(String imageUri) {
URL imgUrl = null;
try {
imgUrl = new URL(imageUri);
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
BitmapDrawable icon = null;
try {
HttpURLConnection hp = (HttpURLConnection) imgUrl.openConnection();
icon = new BitmapDrawable(hp.getInputStream());// 将输入流转换成bitmap
hp.disconnect();// 关闭连接
} catch (Exception e) {
}
return icon;
}

/**
* 根据一个网络连接(URL)获取bitmap图像
*
* @param imageUri
* @return
*/
public static Bitmap getusericon(URL imageUri) {
// 显示网络上的图片
URL myFileUrl = imageUri;
Bitmap bitmap = null;
try {
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}

/**
* 根据一个网络连接(String)获取bitmap图像
*
* @param imageUri
* @return
* @throws MalformedURLException
*/
public static Bitmap getbitmap(String imageUri) {
// 显示网络上的图片
Bitmap bitmap = null;
try {
URL myFileUrl = new URL(imageUri);
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();

Log.i(TAG, “image download finished.” + imageUri);
} catch (IOException e) {
e.printStackTrace();
return null;
}
return bitmap;
}

/**
* 下载图片 同时写道本地缓存文件中
*
* @param context
* @param imageUri
* @return
* @throws MalformedURLException
*/
public static Bitmap getbitmapAndwrite(String imageUri) {
Bitmap bitmap = null;
try {
// 显示网络上的图片
URL myFileUrl = new URL(imageUri);
HttpURLConnection conn = (HttpURLConnection) myFileUrl
.openConnection();
conn.setDoInput(true);
conn.connect();

InputStream is = conn.getInputStream();
File cacheFile = FileUtil.getCacheFile(imageUri);
BufferedOutputStream bos = null;
bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
Log.i(TAG, “write file to ” + cacheFile.getCanonicalPath());

byte[] buf = new byte[1024];
int len = 0;
// 将网络上的图片存储到本地
while ((len = is.read(buf)) > 0) {
bos.write(buf, 0, len);
}

is.close();
bos.close();

// 从本地加载图片
bitmap = BitmapFactory.decodeFile(cacheFile.getCanonicalPath());
String name = MD5Util.MD5(imageUri);

} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}

public static boolean downpic(String picName, Bitmap bitmap) {
boolean nowbol = false;
try {
File saveFile = new File(“/mnt/sdcard/download/weibopic/” + picName
+ “.png”);
if (!saveFile.exists()) {
saveFile.createNewFile();
}
FileOutputStream saveFileOutputStream;
saveFileOutputStream = new FileOutputStream(saveFile);
nowbol = bitmap.compress(Bitmap.CompressFormat.PNG, 100,
saveFileOutputStream);
saveFileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return nowbol;
}

public static void writeTofiles(Context context, Bitmap bitmap,
String filename) {
BufferedOutputStream outputStream = null;
try {
outputStream = new BufferedOutputStream(context.openFileOutput(
filename, Context.MODE_PRIVATE));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

/**
* 将文件写入缓存系统中
*
* @param filename
* @param is
* @return
*/
public static String writefile(Context context, String filename,
InputStream is) {
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
inputStream = new BufferedInputStream(is);
outputStream = new BufferedOutputStream(context.openFileOutput(
filename, Context.MODE_PRIVATE));
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
} catch (Exception e) {
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return context.getFilesDir() + “/” + filename + “.jpg”;
}

// 放大缩小图片
public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidht = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidht, scaleHeight);
Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height,
matrix, true);
return newbmp;
}

// 将Drawable转化为Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, drawable
.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;

}

// 获得圆角图片的方法
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) {
if(bitmap == null){
return null;
}

Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);

final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);

paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}

// 获得带倒影的图片方法
public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap) {
final int reflectionGap = 4;
int width = bitmap.getWidth();
int height = bitmap.getHeight();

Matrix matrix = new Matrix();
matrix.preScale(1, -1);

Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height / 2,
width, height / 2, matrix, false);

Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
(height + height / 2), Config.ARGB_8888);

Canvas canvas = new Canvas(bitmapWithReflection);
canvas.drawBitmap(bitmap, 0, 0, null);
Paint deafalutPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, deafalutPaint);

canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);

Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff,
0x00ffffff, TileMode.CLAMP);
paint.setShader(shader);
// Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()
+ reflectionGap, paint);

return bitmapWithReflection;
}

}

FileUtil

[java]  view plain copy
package com.wangge.coupon.util;

import java.io.File;
import java.io.IOException;

import android.os.Environment;
import android.util.Log;

import com.wangge.coupon.http.AsynImageLoader;

public class FileUtil {
private static final String TAG = “FileUtil”;

public static File getCacheFile(String imageUri){
File cacheFile = null;
try {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File sdCardDir = Environment.getExternalStorageDirectory();
String fileName = getFileName(imageUri);
File dir = new File(sdCardDir.getCanonicalPath()
+ AsynImageLoader.CACHE_DIR);
if (!dir.exists()) {
dir.mkdirs();
}
cacheFile = new File(dir, fileName);
Log.i(TAG, “exists:” + cacheFile.exists() + “,dir:” + dir + “,file:” + fileName);
}
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, “getCacheFileError:” + e.getMessage());
}

return cacheFile;
}

public static String getFileName(String path) {
int index = path.lastIndexOf(“/”);
return path.substring(index + 1);
}
}

 

Android 异步加载解决方案

Android的Lazy Load主要体现在网络数据(图片)异步加载、数据库查询、复杂业务逻辑处理以及费时任务操作导致的异步处理等方面。在介绍Android开发过程中,异步处理这个常见的技术问题之前,我们简单回顾下Android开发过程中需要注意的几个地方。
Android应用开发过程中必须遵循单线程模型(Single Thread Model)的原则。因为Android的UI操作并不是线程安全的,所以涉及UI的操作必须在UI线程中完成。但是并非所有的操作都能在主线程中进行,Google工程师在设计上约定,Android应用在5s内无响应的话会导致ANR(Application Not Response),这就要求开发者必须遵循两条法则:1、不能阻塞UI线程,2、确保只在UI线程中访问Android UI工具包。于是,开启子线程进行异步处理的技术方案应运而生。

本文以自定义ListView,异步加载网络图片示例,总结了Android开发过程中,常用的三种异步加载的技术方案。

相关资源:

AndroidManifest.xml

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”
package=”com.example.loadload”
android:versionCode=”1″
android:versionName=”1.0″ >

<uses-sdk
android:minSdkVersion=”8″
android:targetSdkVersion=”15″ />

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

<application
android:icon=”@drawable/ic_launcher”
android:label=”@string/app_name”
android:theme=”@style/AppTheme” >
<activity android:name=”.ThreadHandlerPostActivity” >
</activity>
<activity android:name=”.AsyncTastActivity” >
</activity>
<activity android:name=”.ThreadHandlerActivity” >
</activity>
<activity android:name=”com.doodle.asynctasksample.BootActivity” >
<intent-filter>
<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
</activity>
</application>

</manifest>

list_item.xml

<RelativeLayout 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” >

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”150dp”
android:layout_alignParentLeft=”true”
android:layout_alignParentRight=”true”
android:layout_alignParentTop=”true” >

<ImageView
android:id=”@+id/imageView”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:src=”@drawable/ic_launcher” />
</LinearLayout>

</RelativeLayout>

ImageAdapter.java
public class ImageAdapter extends BaseAdapter
{
private Context context;
private List<HashMap<String, Object>> listItems;
private LayoutInflater listContainer;

public ImageView imageView;

public ImageAdapter(Context context, List<HashMap<String, Object>> listItems)
{
super();
this.context = context;
this.listContainer = LayoutInflater.from(context);
this.listItems = listItems;
}

@Override
public int getCount()
{
return listItems.size();
}

@Override
public Object getItem(int position)
{
return null;
}

@Override
public long getItemId(int position)
{
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = listContainer.inflate(R.layout.list_item, null);
imageView = (ImageView) convertView.findViewById(R.id.imageView);
convertView.setTag(imageView);
}
else
{
imageView = (ImageView) convertView.getTag();
}
imageView.setImageDrawable((Drawable) listItems.get(position).get(
“ItemImage”));
return convertView;
}
}

一、采用AsyncTask

AsyncTask简介 AsyncTask的特点是任务在主线程之外运行,而回调方法是在主线程中执行,这就有效地避免了使用Handler带来的麻烦。阅读 AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的,concurrent框架是一个非常 成熟,高效的框架,经过了严格的测试。这说明AsyncTask的设计很好的解决了匿名线程存在的问题。 AsyncTask是抽象类,其结构图如下图所示: AsyncTask定义了三种泛型类型 Params,Progress和Result。 Params 启动任务执行的输入参数,比如HTTP请求的URL。 Progress 后台任务执行的百分比。 Result 后台执行任务*终返回的结果,比如String。 子类必须实现抽象方法doInBackground(Params… p) ,在此方法中实现任务的执行工作,比如连接网络获取数据等。通常还应 该实现onPostExecute(Result r)方法,因为应用程序关心的结果在此方法中返回。需要注意的是AsyncTask一定要在主线程中创 建实例。 AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,需要注意的是这些方法不应该由应用程序调用,开发者需要做的 就是实现这些方法。在任务的执行过程中,这些方法被自动调用,运行过程,如下图所示: onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。 doInBackground(Params…) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用 publicProgress(Progress…)来更新任务的进度。 onProgressUpdate(Progress…) 此方法在主线程执行,用于显示任务执行的进度。 onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回
There are a few threading rules that must be followed for this class to work properly: The AsyncTask class must be loaded on the UI thread. This is done automatically as of JELLY_BEAN. The task instance must be created on the UI thread. execute(Params…) must be invoked on the UI thread. Do not call onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) manually. The task can be executed only once (an exception will be thrown if a second execution is attempted.)

AsyncTastActivity.java

public class AsyncTastActivity extends Activity
{

private List<String> urlList;
private ImageAdapter listItemAdapter;
private ArrayList<HashMap<String, Object>> listItem;

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlList = new ArrayList<String>();
urlList.add(“http://www.baidu.com/img/baidu_sylogo1.gif”);
urlList.add(“http://y2.ifengimg.com/2012/06/24/23063562.gif”);
urlList.add(“http://himg2.huanqiu.com/statics/images/index/logo.png”);

listItem = new ArrayList<HashMap<String, Object>>();

listItemAdapter = new ImageAdapter(this, listItem);
ListView listView = (ListView) this.findViewById(R.id.listView1);
listView.setAdapter(listItemAdapter);

AsyncTask<List<String>, Integer, Hashtable<String, SoftReference<Drawable>>> task = new AsyncTask<List<String>, Integer, Hashtable<String, SoftReference<Drawable>>>() {

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

@Override
protected Hashtable<String, SoftReference<Drawable>> doInBackground(
List<String>… params)
{
Hashtable<String, SoftReference<Drawable>> table = new Hashtable<String, SoftReference<Drawable>>();
List<String> imageUriList = params[0];
for (String urlStr : imageUriList)
{
try
{
URL url = new URL(urlStr);
Drawable drawable = Drawable.createFromStream(
url.openStream(), “src”);
table.put(urlStr, new SoftReference<Drawable>(drawable));
}
catch (Exception e)
{
e.printStackTrace();
}
}
return table;
}

@Override
protected void onPostExecute(
Hashtable<String, SoftReference<Drawable>> result)
{
super.onPostExecute(result);
Collection<SoftReference<Drawable>> col = result.values();
for (SoftReference<Drawable> ref : col)
{
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(“ItemImage”, ref.get());
listItem.add(map);
}
listItemAdapter.notifyDataSetChanged();

}
};

task.execute(urlList);
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}

二、采用Thread + Handler + Message

Handler简介 Handler为Android提供了一种异步消息处理机制,它包含两个队列,一个是线程列队,另一个是消息列队。使用post方法将线 程对象添加到线程队列中,使用sendMessage(Message message)将消息放入消息队列中。当向消息队列中发送消息后就立 即返回,而从消息队列中读取消息对象时会阻塞,继而回调Handler中public void handleMessage(Message msg)方法。因此 在创建Handler时应该使用匿名内部类重写该方法。如果想要这个流程一直执行的话,可以再run方法内部执行postDelay或者 post方法,再将该线程对象添加到消息队列中重复执行。想要停止线程,调用Handler对象的removeCallbacks(Runnable r)从 线程队列中移除线程对象,使线程停止执行。
ThreadHandlerActivity.java
public class ThreadHandlerActivity extends Activity
{

private List<String> urlList;
private ImageAdapter listItemAdapter;
private LinkedList<HashMap<String, Object>> listItem;
private Handler handler;
private ExecutorService executorService = Executors.newFixedThreadPool(10);

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlList = new ArrayList<String>();
urlList.add(“http://www.baidu.com/img/baidu_sylogo1.gif”);
urlList.add(“http://y2.ifengimg.com/2012/06/24/23063562.gif”);
urlList.add(“http://himg2.huanqiu.com/statics/images/index/logo.png”);

listItem = new LinkedList<HashMap<String, Object>>();

listItemAdapter = new ImageAdapter(this, listItem);
ListView listView = (ListView) this.findViewById(R.id.listView1);
listView.setAdapter(listItemAdapter);

handler = new Handler() {
@Override
public void handleMessage(Message msg)
{
HashMap<String, Object> map = (HashMap<String, Object>) msg.obj;
listItem.add(map);
listItemAdapter.notifyDataSetChanged();
}
};
for (final String urlStr : urlList)
{
executorService.submit(new Runnable() {
@Override
public void run()
{
try
{
URL url = new URL(urlStr);
Drawable drawable = Drawable.createFromStream(
url.openStream(), “src”);
HashMap<String, Object> table = new HashMap<String, Object>();
table.put(“ItemImage”, drawable);
Message msg = new Message();
msg.obj = table;
msg.setTarget(handler);
handler.sendMessage(msg);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}

三、采用Thread + Handler + post方法

使用post方法将Runnable对象放到Handler的线程队列中,该Runnable的执行其实并未单独开启线程,而是仍然在当前Activity的UI线程中执行,Handler只是调用了Runnable对象的run方法。
ThreadHandlerPostActivity.java

public class ThreadHandlerPostActivity extends Activity

{

private List<String> urlList;
private ImageAdapter listItemAdapter;
private LinkedList<HashMap<String, Object>> listItem;
private Handler handler = new Handler();
private ExecutorService executorService = Executors.newFixedThreadPool(10);

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urlList = new ArrayList<String>();
urlList.add(“http://www.baidu.com/img/baidu_sylogo1.gif”);
urlList.add(“http://y2.ifengimg.com/2012/06/24/23063562.gif”);
urlList.add(“http://himg2.huanqiu.com/statics/images/index/logo.png”);

listItem = new LinkedList<HashMap<String, Object>>();

listItemAdapter = new ImageAdapter(this, listItem);
ListView listView = (ListView) this.findViewById(R.id.listView1);
listView.setAdapter(listItemAdapter);

for (final String urlStr : urlList)
{
executorService.submit(new Runnable() {
@Override
public void run()
{
try
{
URL url = new URL(urlStr);
Drawable drawable = Drawable.createFromStream(
url.openStream(), “src”);
final HashMap<String, Object> table = new HashMap<String, Object>();
table.put(“ItemImage”, drawable);
handler.post(new Runnable() {

@Override
public void run()
{
listItem.add(table);
listItemAdapter.notifyDataSetChanged();
}

});
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}

综上所述,我们可以看出,Android API中AsyncTask对于异步处理不是万能的,对于需要循环、多次的任务处理,我们任然需要采用传统的Thread线程机制。我们可以根据需要,灵活取舍。

Android WebView学习资料

首先要在manifest.main文件中创建一个webview,然后再activity中定义这个webview然后
进行一下相关操作。

1、添加权限:AndroidManifest.xml中必须使用许可”android.permission.INTERNET”,否则会出
Web page not available错误。

2、在要Activity中生成一个WebView组件:WebView webView = new WebView(this);

3、设置WebView基本信息:

如果访问的页面中有Javascript,则webview必须设置支持Javascript。

webview.getSettings().setJavaScriptEnabled(true);

触摸焦点起作用

requestFocus();

取消滚动条

this.setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);

4如果希望点击链接由自己处理,而不是新开Android的系统browser中响应该链接。给
WebView添加一个事件监听对象(WebViewClient)并重写其中的一些方法
shouldOverrideUrlLoading:对网页中超链接按钮的响应。 当按下某个连接时WebViewClient
会调用这个方法,并传递参数:按下的url

onLoadResource

onPageStart

onPageFinish

onReceiveError

onReceivedHttpAuthRequest

5、如果访问的页面中有Javascript,则webview必须设置支持Javascript ,否则显示空白页面。

Java代码

webview.getSettings().setJavaScriptEnabled(true);
6、如果页面中链接,如果希望点击链接继续在当前browser中响应,而不是新开Android的系统browser
中响应该链接,必须覆盖webview的WebViewClient对象:

Java代码

1. mWebView.setWebViewClient(new WebViewClient(){
2. public boolean shouldOverrideUrlLoading(WebView view, String url) {
3. view.loadUrl(url);
4. return true;
5. }
6. });

上述方法告诉系统由我这个WebViewClient处理这个Intent,我来加载URL。点击一个链接的Intent是向上
冒泡的,shouldOverrideUrlLoading方法return true表示我加载后这个Intent就消费了,不再向上冒泡了。
7、如果不做任何处理,在显示你的Brower UI时,点击系统“Back”键,整个Browser会作为一个整体“Back”

到其他Activity中,而不是希望的在Browser的历史页面中 Back。如果希望实现在历史页面中Back,需
要在当前Activity中处理并消费掉该Back事件:

Java代码

1. public boolean onKeyDown(int keyCode, KeyEvent event) {
2. if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
3. mWebView.goBack();
4. return true;
5. }
6. return super.onKeyDown(keyCode, event);
7. }

对于Android 2.0开始又多出了一种新的方法,对于Activity 可以单独获取Back键的按下事件,直接重
写 onBackPressed 方法即可,代码如下

Java代码

@Override
1、public void onBackPressed() {
2、 // 这里处理逻辑代码,该方法仅适用于2.0或更高版本的sdk
3、 return ;
4、}

 

这里还有几个知识点:

 

1)为了让WebView从apk文件中加载 assets,Android SDK提供了一个schema,前缀为
“file:///android_asset/”。WebView遇到这样的schema,就去当前包中的 assets目录中找内
容。如上面的”file:///android_asset/demo.html”

 

2)addJavascriptInterface方法中要绑定的 Java对象及方法要运行另外的线程中,不能运行在
构造他的线程中,这也是使用Handler的目的。

 

Webview的两种显示网页的方法:

(1):webview.loadUrl(“www.baidu.com”);

public class WebviewTest extends Activity {

/** Called when the activity is first created. */

 

private WebView mWebView;

private Button bt1;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mWebView = (WebView)findViewById(R.id.webview);

WebSettings webSettings = mWebView.getSettings();

webSettings.setJavaScriptEnabled(true);

 

bt1 = (Button)findViewById(R.id.Button01);

 

bt1.setOnClickListener(new View.OnClickListener() {

 

 

public void onClick(View arg0) {

// TODO Auto-generated method stub

webview.loadUrl(“www.baidu.com”);

}

});

}

}

 

(2):自定义网页:Webview.data();

 

public class WebviewTest extends Activity {

/** Called when the activity is first created. */

 

private WebView mWebView;

private Button bt1;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mWebView = (WebView)findViewById(R.id.webview);

WebSettings webSettings = mWebView.getSettings();

webSettings.setJavaScriptEnabled(true);

 

bt1 = (Button)findViewById(R.id.Button01);

 

bt1.setOnClickListener(new View.OnClickListener() {

 

 

public void onClick(View arg0) {

// TODO Auto-generated method stub

String str = “asdas”;

mWebView.loadData(

“<html><body>”+str+”</body></html>”,

“text/html”, “utf-8″);

}

});

}

}

 

Webview的常用方法实例:

 

Manifest.xml中的代码:

加入权限:

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

布局文件中的代码:

<?xml version=”1.0″ encoding=”utf-8″?>

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical” android:layout_width=”fill_parent”

android:layout_height=”fill_parent”>

<WebView android:id=”@+id/WebView01″ android:layout_width=”fill_parent”

android:layout_height=”fill_parent”></WebView>

</LinearLayout>

 

TestWebviewDemo中的代码:

public class TestWebviewDemo extends Activity {

/** Called when the activity is first created. */

private static final String TAG = “TestWebviewDemo”;

private WebView mWebView;

private Handler mHandler = new Handler();

private int mDensity;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

 

mWebView = (WebView) findViewById(R.id.WebView01);

mWebView.getSettings().setAllowFileAccess(true);// 设置允许访问文件数据

mWebView.getSettings().setBuiltInZoomControls(true);// 设置支持缩放

mWebView.getSettings().setSavePassword(false); // 设置是否保存密码

// 设置支持JavaScript脚本

mWebView.getSettings().setJavaScriptEnabled(true);

// 设置支持各种不同的设备

mWebView

.getSettings()

.setUserAgentString(

“Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)
AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10”);

// 通过这个设置来执行加载webview网页时所要执行的一些方法

mWebView.setWebViewClient(new WebViewClient() {

// 新开页面时用自己定义的webview来显示,不用系统自带的浏览器来显示

public boolean shouldOverrideUrlLoading(WebView view, String url) {

// TODO Auto-generated method stub

// 当有新连接时使用当前的webview进行显示

view.loadUrl(url);

return super.shouldOverrideUrlLoading(view, url);

}

// 开始加载网页时要做的工作

public void onPageStarted(WebView view, String url, Bitmap favicon) {

 

super.onPageStarted(view, url, favicon);

}

//加载完成时要做的工作

public void onPageFinished(WebView view, String url) {

 

super.onPageFinished(view, url);

}

// 加载错误时要做的工作

public void onReceivedError(WebView view, int errorCode,

String description, String failingUrl) {

Log.d(TAG, “error=” + description);

Toast.makeText(TestWebviewDemo.this,

errorCode + “/” + description, Toast.LENGTH_LONG)

.show();

}

});

// 处理网页中的一些对话框信息(提示对话框,带选择的对话框,带输入的对话
框)

mWebView.setWebChromeClient(new WebChromeClient() {

// 对话框

public boolean onJsAlert(WebView view, String url, String message,

final JsResult result) {

// 构建一个Builder来显示网页中的alert对话框

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“提示对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.confirm();

}

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

// 带按钮的对话框

public boolean onJsConfirm(WebView view, String url,

String message, final JsResult result) {

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“带选择的对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.confirm();

}

 

});

builder.setNeutralButton(android.R.string.cancel,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

// 带输入框的对话框

public boolean onJsPrompt(WebView view, String url, String message,

String defaultValue, final JsPromptResult result) {

LayoutInflater inflater = LayoutInflater

.from(TestWebviewDemo.this);

final View v = inflater.inflate(R.layout.prom_dialog, null);

// 设置 TextView对应网页中的提示信息

((TextView) v.findViewById(R.id.TextView_PROM))

.setText(message);

// 设置EditText对应网页中的输入框

((EditText) v.findViewById(R.id.EditText_PROM))

.setText(defaultValue);

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“带输入的对话框”);

builder.setView(v);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

String value = ((EditText) v

.findViewById(R.id.EditText_PROM))

.getText().toString();

result.confirm(value);

}

});

builder.setNegativeButton(android.R.string.cancel,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.cancel();

}

});

builder

.setOnCancelListener(new DialogInterface.OnCancelListener() {

 

@Override

public void onCancel(DialogInterface dialog) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.create();

builder.show();

return true;

 

}

 

// 设置网页加载的进度条

public void onProgressChanged(WebView view, int newProgress) {

TestWebviewDemo.this.getWindow().setFeatureInt(

Window.FEATURE_PROGRESS, newProgress * 100);

super.onProgressChanged(view, newProgress);

}

 

// 设置应用程序的标题

public void onReceivedTitle(WebView view, String title) {

TestWebviewDemo.this.setTitle(title);

super.onReceivedTitle(view, title);

}

 

});

// 与网页进行交互的addJavascriptInterface()的方法

mWebView.addJavascriptInterface(new Object() {

public void clickOnAndroid(final String str) {

mHandler.post(new Runnable() {

@Override

public void run() {

// 逻辑代码

});

}

}, “demo”);

 

if (mDensity == 240) { // 可以让不同的density的情况下,可以让页面进行适配

mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR);

} else if (mDensity == 160) {

mWebView.getSettings().setDefaultZoom(ZoomDensity.MEDIUM);

} else {

mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE);

}

String strUrl = “http://10.0.2.2:8080/WebTest3″;

mWebView.loadUrl(strUrl);

}

//处理返回的事件,(后退进入前一个界面而不是全部退出)

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {

mWebView.goBack();// 返回前一个页面

return true;

}

return super.onKeyDown(keyCode, event);

}

 

 

 

 

Webview中java与网页进行数据交互:

先看我们的html文档:

<html>

<script language=”javascript”>

/* This function is invoked by the activity */

function wave() {

alert(“1”);

document.getElementById(“droid”).src=”android_waving.png”;

alert(“2″);

}

</script>

<body>

<!– Calls into the javascript interface for the activity –>

//js调用Java方法

<a onClick=”window.demo.clickOnAndroid()”><div style=”width:80px;

margin:0px auto;

padding:10px;

text-align:center;

border:2px solid #202020;” >

<img id=”droid” src=”android_normal.png”/><br>

Click me!

</div></a>

</body>

</html>

 

Manifest.Xml中:

加入权限:

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

 

再看我们的java 代码。

Java代码

public class WebViewDemo extends Activity {

 

private static final String LOG_TAG = “WebViewDemo”;

 

private WebView mWebView;

 

private Handler mHandler = new Handler();

 

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

mWebView = (WebView) findViewById(R.id.webview);

 

WebSettings webSettings = mWebView.getSettings();

webSettings.setSavePassword(false);

webSettings.setSaveFormData(false);

webSettings.setJavaScriptEnabled(true);

webSettings.setSupportZoom(false);

 

mWebView.setWebChromeClient(new MyWebChromeClient());

//自定义的Demo,供js网页调用

mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), “demo”);

 

mWebView.loadUrl(“file:///android_asset/demo.html”);

}

final class DemoJavaScriptInterface {

 

DemoJavaScriptInterface() {

}

 

/**

* This is not called on the UI thread. Post a runnable to invoke

* loadUrl on the UI thread.

*/

public void clickOnAndroid() {

//用handler来更新UI

mHandler.post(new Runnable() {

public void run() {

//Java调用js方法

mWebView.loadUrl(“javascript:wave()”);

}

});

}

}

/**

* Provides a hook for calling “alert” from javascript. Useful for

* debugging your javascript.

*/

final class MyWebChromeClient extends WebChromeClient {

@Override

public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

Log.d(LOG_TAG, message);

result.confirm();

return true;

}

 

}

}

 

 

再看个例子:

Html中的代码:

<script type=”text/JavaScript”>

function s(){

alert(“Good Morning!”);

}

function d(){

confirm(“Are you ok?”)

}

function f(){

prompt(“What’s your name?”)

}

</script>

</head>

 

<body>

<body>

下面我们演示三种对话框

<br/><br/>

<input type=”button” value=”警告,提醒对话框” οnclick=”s()”>

<br/><br/>

<input type=”button” value=”带选择的对话框” οnclick=”d()”>

<br/><br/>

<input type=”button” value=”要求用户输入的对话框” οnclick=”f()”>

</body>

 

Manifest.Xml中:

加入权限:

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

 

Java中的代码:

public class Test extends Activity {

/** Called when the activity is first created. */

private EditText et = null;

private Button btn = null;

private WebView wv = null;

private WebSettings ws = null;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

et = (EditText) this.findViewById(R.id.EditText01);

btn = (Button) this.findViewById(R.id.Button01);

wv = (WebView) this.findViewById(R.id.WebView);

ws = wv.getSettings();

ws.setAllowFileAccess(true);//设置允许访问文件数据

ws.setJavaScriptEnabled(true);//设置支持javascript脚本

ws.setBuiltInZoomControls(true);//设置支持缩放

wv.requestFocus();

wv.setWebViewClient(new WebViewClient(){

public boolean shouldOverrideUrlLoading(WebView view,String url){

//当有新连接时,使用当前的 WebView

view.loadUrl(url);

return true;

}

});

wv.setWebChromeClient(new WebChromeClient(){

public boolean onJsAlert(WebView view,String url,String message,final JsResult
result){

//构建一个Builder来显示网页中的alert对话框

Builder builder = new Builder(Test.this);

builder.setTitle(“提示对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok, new
AlertDialog.OnClickListener(){

 

 

@Override

public void onClick(DialogInterface dialog, int which) {

// TODO Auto-generated method stub

result.confirm();

}

 

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

public boolean onJsConfirm(WebView view,String url,String message,final
JsResult result){

Builder builder = new Builder(Test.this);

builder.setTitle(“带选择的对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok, new
AlertDialog.OnClickListener(){

 

@Override

public void onClick(DialogInterface dialog, int which) {

// TODO Auto-generated method stub

result.confirm();

}

 

});

builder.setNeutralButton(android.R.string.cancel, new
AlertDialog.OnClickListener(){

 

@Override

public void onClick(DialogInterface dialog, int which) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

public boolean onJsPrompt(WebView view,String url,String message,String
defaultValue,final JsPromptResult result){

LayoutInflater inflater = LayoutInflater.from(Test.this);

final View v = inflater.inflate(R.layout.prom_dialog, null);

//设置 TextView对应网页中的提示信息

((TextView)v.findViewById(R.id.TextView_PROM)).setText(message);

//设置EditText对应网页中的输入框

((EditText)v.findViewById(R.id.EditText_PROM)).setText(defaultValue);

Builder builder = new Builder(Test.this);

builder.setTitle(“带输入的对话框 “);

builder.setView(v);

builder.setPositiveButton(android.R.string.ok, new

AlertDialog.OnClickListener(){

 

@Override

public void onClick(DialogInterface dialog, int which) {

// TODO Auto-generated method stub

String value =
((EditText)v.findViewById(R.id.EditText_PROM)).getText().toString();

result.confirm(value);

}

 

});

builder.setNegativeButton(android.R.string.cancel, new
AlertDialog.OnClickListener(){

 

@Override

public void onClick(DialogInterface dialog, int which) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.setOnCancelListener(new DialogInterface.OnCancelListener(){

 

@Override

public void onCancel(DialogInterface dialog) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.create();

builder.show();

return true;

}

//设置网页加载的进度条

public void onProgressChanged(WebView view,int newProgress){

Test.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
newProgress*100);

super.onProgressChanged(view, newProgress);

}

//设置应用程序的标题

public void onReceivedTitle(WebView view,String title){

Test.this.setTitle(title);

super.onReceivedTitle(view, title);

}

});

btn.setOnClickListener(new Button.OnClickListener(){

 

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

String url =et.getText().toString();

// String url = “http://10.0.2.2:8080/WebTest3”;

//判断输入的内容是不是网址

if(URLUtil.isNetworkUrl(url)){

Log.d(“++++++++++++”, “sadas”);

wv.loadUrl(url);

Toast.makeText(Test.this, url, Toast.LENGTH_SHORT).show();

}else{

et.setHint(“输入的网址不合法,请重新输入”);

// et.setText(“输入的网址不合法,请重新输入”);

}

}

 

});

}

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

if(keyCode==KeyEvent.KEYCODE_BACK && wv.canGoBack()){

wv.goBack();//返回前一个页面

return true;

}

return super.onKeyDown(keyCode, event);

}

 

}

 

 

 

需要注意的地方,很多数据类型js中不认识,*好是在android那边封装好,提供必要的方
法接口。比如传到js中的list,在js中是没办法去得到里面的元素的。

 

Manifest.Xml中:

加入权限:

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

 

html的带码

<script language=”javascript”>

window.οnlοad= function(){

var i=window.javatojs.getSize();

for(var n=0;n<i;n++){

var jsdata= window.javatojs.getObject(n);//拿到activity里面的属性javadata

var datalistdiv = document.getElementById(“datalist”); //得到页面的div

pnode = document.createElement(“p”);//创建一个p标签,再建个textnode

tnode = document.createTextNode(jsdata);

pnode.appendChild(tnode);//p中加入数据

datalistdiv.appendChild(pnode);//div中键入新的p

}

}

</script>

<body>

<div id = “datalist”>

 

this is a demo

</body>

 

Java的代码:

public class JavaToWebview extends Activity {

 

private WebView web;

public List list;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

initData();

setContentView(R.layout.webview);

web = (WebView)this.findViewById(R.id.webview);

web.getSettings().setJavaScriptEnabled(true);//开启javascript设置

web.addJavascriptInterface(this, “javatojs”);//把RIAExample的一个实例添加到js的全
局对象window中, //这样就可以使用window.javatojs来调用它的方法

web.loadUrl(“file:///android_asset/demo5.html”);//加载网页

 

}

void initData(){

list=new ArrayList<String>();

for(int i=0;i<5;i++){

list.add(“我是从数据库中读取的哈哈”);

}

}

/**

* 该方法将在js脚本中,通过window.javatojs…..()进行调用

* @return

*/

public Object getObject(int index){

return list.get(index);

}

public int getSize(){

return list.size();

}

}

 

再看一个查地图的例子:

Manifest.Xml中:

加入权限:

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

布局文件中的代码:

<?xml version=”1.0″ encoding=”utf-8″?>

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

>

<TextView

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Welcome to Mr Wei’s Blog.”

/>

<WebView

android:id=”@+id/webview”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

/>

<Button

android:id=”@+id/button”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Change the webview content”

/>

</LinearLayout>

在assets目录下新建一个demo.html文件,代码如下:

<html>

<script language=”javascript”><!–

 

function fillContent(){

document.getElementById(“content”).innerHTML =

“This Content is showed by Android invoke Javascript function.”;

}

 

// –></script>

<body>

<p><a onClick=”window.demo.startMap()” href=””>Start GoogleMap</a></p>

<p id=”content”></p>

<p>A Demo —-Android and Javascript invoke each other.</p>

<p>Author:Frankiewei</p>

</body>

</html>

 

activity代码:

public class WebViewDemo extends Activity {

private WebView mWebView;

private Button mButton;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

setupViews();

}

//初始化

private void setupViews() {

mWebView = (WebView) findViewById(R.id.webview);

WebSettings mWebSettings = mWebView.getSettings();

//加上这句话才能使用javascript方法

mWebSettings.setJavaScriptEnabled(true);

//增加接口方法,让html页面调用

mWebView.addJavascriptInterface(new Object() {

//这里我定义了一个打开地图应用的方法

public void startMap() {

Intent mIntent = new Intent();

ComponentName component = new ComponentName(

“com.google.android.apps.maps”,

“com.google.android.maps.MapsActivity”);

mIntent.setComponent(component);

startActivity(mIntent);

}

}, “demo”);

//加载页面

mWebView.loadUrl(“file:///android_asset/demo.html”);

mButton = (Button) findViewById(R.id.button);

//给button添加事件响应,执行JavaScript的fillContent()方法

mButton.setOnClickListener(new Button.OnClickListener() {

public void onClick(View v) {

mWebView.loadUrl(“javascript:fillContent()”);

}

});

}

}

 

 

其他例子如下:(activity中获取js界面输入框的值)

Jsp中的代码:

<body>

<form action=”” method=”post”>

宝宝预产期:<br>

<select id=”shijian” name=”date”>

<option value=”2006″>2006</option>

<option value=”2007″>2007</option>

<option value=”2008″>2008</option>

<option value=”2009″>2009</option>

<option value=”2010″>2010</option>

</select><br>

常用邮箱号:

<input id=”email” type=”text” name=”emailID” />

<br>

宝宝昵称:

<input id=”name” type=”text” name=”username” />

<br>

宝宝性别:<br>

<input id=”men” type=”radio” name=”sex” value=”men”/>男

<input id=”women” type=”radio” name=”sex” value=”women”/>女

<br>

<input type=”submit” value=”注册”

οnclick=”f()”/>

<input type=”button” value=”取消” />

</form>

</body>

<script type=”text/JavaScript” language=”javascript”>

function f(){

var email = document.getElementById(’email’).value;

var name = document.getElementById(‘name’).value;

var date = document.getElementById(‘shijian’).value;

if(document.getElementById(‘men’).checked
&& !document.getElementById(‘women’).checked){

var sex = document.getElementById(‘men’).value;

}else if(!document.getElementById(‘men’).checked &&
document.getElementById(‘women’).checked){

var sex = document.getElementById(‘women’).value;

}

window.demo.clickOnAndroid(date+”|”+email+”|”+name+”|”+sex);

}

</script>

Manifest.xml中的代码:

<?xml version=”1.0″ encoding=”utf-8″?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.ruixin.login” android:versionCode=”1″ android:versionName=”1.0″>

<application android:icon=”@drawable/icon” android:label=”@string/app_name”>

<activity android:name=”.TestWebviewDemo” android:label=”@string/app_name”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

<intent-filter>

<data android:mimeType=”vnd.android.cursor.dir/vnd.ruixin.login” />

</intent-filter>

<intent-filter>

<data android:mimeType=”vnd.android.cursor.item/vnd.ruixin.login” />

</intent-filter>

 

</activity>

<provider android:name=”MyProvider” android:authorities=”com.ruixin.login” />

</application>

<uses-sdk android:minSdkVersion=”8″ />

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

<uses-permission
android:name=”android.permission.READ_CONTACTS”></uses-permission>

</manifest>

 

 

布局文件中的代码:

<?xml version=”1.0″ encoding=”utf-8″?>

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical” android:layout_width=”fill_parent”

android:layout_height=”fill_parent”>

<WebView android:id=”@+id/WebView01″ android:layout_width=”fill_parent”

android:layout_height=”fill_parent”></WebView>

</LinearLayout>

 

TestWebviewDemo中的代码:

public class TestWebviewDemo extends Activity {

/** Called when the activity is first created. */

private static final String TAG = “TestWebviewDemo”;

private WebView mWebView;

private Handler mHandler = new Handler();

private int mDensity;

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

 

mWebView = (WebView) findViewById(R.id.WebView01);

mWebView.getSettings().setAllowFileAccess(true);// 设置允许访问文件数据

mWebView.getSettings().setBuiltInZoomControls(true);// 设置支持缩放

mWebView.getSettings().setSavePassword(false); // 设置是否保存密码

// 设置支持JavaScript脚本

mWebView.getSettings().setJavaScriptEnabled(true);

// 设置支持各种不同的设备

mWebView

.getSettings()

.setUserAgentString(

“Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)
AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10”);

// 通过这个设置来执行加载webview网页时所要执行的一些方法

mWebView.setWebViewClient(new WebViewClient() {

// 新开页面时用自己定义的webview来显示,不用系统自带的浏览器来显示

public boolean shouldOverrideUrlLoading(WebView view, String url) {

// TODO Auto-generated method stub

// 当有新连接时使用当前的webview进行显示

view.loadUrl(url);

return super.shouldOverrideUrlLoading(view, url);

}

// 开始加载网页时要做的工作

public void onPageStarted(WebView view, String url, Bitmap favicon) {

 

super.onPageStarted(view, url, favicon);

}

//加载完成时要做的工作

public void onPageFinished(WebView view, String url) {

 

super.onPageFinished(view, url);

}

// 加载错误时要做的工作

public void onReceivedError(WebView view, int errorCode,

String description, String failingUrl) {

Log.d(TAG, “error=” + description);

Toast.makeText(TestWebviewDemo.this,

errorCode + “/” + description, Toast.LENGTH_LONG)

.show();

}

});

// 处理网页中的一些对话框信息(提示对话框,带选择的对话框,带输入的对话
框)

mWebView.setWebChromeClient(new WebChromeClient() {

// 对话框

public boolean onJsAlert(WebView view, String url, String message,

final JsResult result) {

// 构建一个Builder来显示网页中的alert对话框

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“提示对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.confirm();

}

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

// 带按钮的对话框

public boolean onJsConfirm(WebView view, String url,

String message, final JsResult result) {

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“带选择的对话框”);

builder.setMessage(message);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.confirm();

}

 

});

builder.setNeutralButton(android.R.string.cancel,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.setCancelable(false);

builder.create();

builder.show();

return true;

}

// 带输入框的对话框

public boolean onJsPrompt(WebView view, String url, String message,

String defaultValue, final JsPromptResult result) {

LayoutInflater inflater = LayoutInflater

.from(TestWebviewDemo.this);

final View v = inflater.inflate(R.layout.prom_dialog, null);

// 设置 TextView对应网页中的提示信息

((TextView) v.findViewById(R.id.TextView_PROM))

.setText(message);

// 设置EditText对应网页中的输入框

((EditText) v.findViewById(R.id.EditText_PROM))

.setText(defaultValue);

Builder builder = new Builder(TestWebviewDemo.this);

builder.setTitle(“带输入的对话框”);

builder.setView(v);

builder.setPositiveButton(android.R.string.ok,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

String value = ((EditText) v

.findViewById(R.id.EditText_PROM))

.getText().toString();

result.confirm(value);

}

});

builder.setNegativeButton(android.R.string.cancel,

new AlertDialog.OnClickListener() {

 

@Override

public void onClick(DialogInterface dialog,

int which) {

// TODO Auto-generated method stub

result.cancel();

}

});

builder

.setOnCancelListener(new DialogInterface.OnCancelListener() {

 

@Override

public void onCancel(DialogInterface dialog) {

// TODO Auto-generated method stub

result.cancel();

}

 

});

builder.create();

builder.show();

return true;

 

}

 

// 设置网页加载的进度条

public void onProgressChanged(WebView view, int newProgress) {

TestWebviewDemo.this.getWindow().setFeatureInt(

Window.FEATURE_PROGRESS, newProgress * 100);

super.onProgressChanged(view, newProgress);

}

 

// 设置应用程序的标题

public void onReceivedTitle(WebView view, String title) {

TestWebviewDemo.this.setTitle(title);

super.onReceivedTitle(view, title);

}

 

});

// 与网页进行交互的addJavascriptInterface()的方法

mWebView.addJavascriptInterface(new Object() {

public void clickOnAndroid(final String str) {

mHandler.post(new Runnable() {

@Override

public void run() {

StringTokenizer stringTokenizer = new StringTokenizer(

str, “|”);

date = stringTokenizer.nextToken();

email = stringTokenizer.nextToken();

username = stringTokenizer.nextToken();

sex = stringTokenizer.nextToken();

//将数据出入数据库

dBlite1.add(email,username,date,sex);

// 用contentResolver访问存入到contentprovider中的数据

contentResolver = TestWebviewDemo.this

.getContentResolver();

Log.d(“++++”, RuiXin.CONTENT_URI.toString());

 

Cursor cursor = contentResolver.query(

RuiXin.CONTENT_URI, new String[] {

RuiXin.EMAIL, RuiXin.USERNAME,

RuiXin.DATE,RuiXin.SEX }, null, null, null);

Log.d(“@@@—-“, cursor.toString());

// startManagingCursor(cursor);

while (cursor.moveToNext()) {

Toast.makeText(

TestWebviewDemo.this,

cursor.getString(cursor.getColumnIndex(RuiXin.EMAIL))

+ ” ”

+ cursor.getString(cursor

.getColumnIndex(RuiXin.USERNAME))

+ ” ”

+ cursor.getString(cursor

.getColumnIndex(RuiXin.DATE))

+ ” ”

+ cursor.getString(cursor

.getColumnIndex(RuiXin.SEX)),

Toast.LENGTH_SHORT).show();

}

startManagingCursor(cursor); //关闭游标

}

});

}

}, “demo”);

 

if (mDensity == 240) { // 可以让不同的density的情况下,可以让页面进行适配

mWebView.getSettings().setDefaultZoom(ZoomDensity.FAR);

} else if (mDensity == 160) {

mWebView.getSettings().setDefaultZoom(ZoomDensity.MEDIUM);

} else {

mWebView.getSettings().setDefaultZoom(ZoomDensity.CLOSE);

}

String strUrl = “http://10.0.2.2:8080/WebTest3”;

mWebView.loadUrl(strUrl);

}

//处理返回的事件,(后退进入前一个界面而不是全部退出)

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {

mWebView.goBack();// 返回前一个页面

return true;

}

return super.onKeyDown(keyCode, event);

}

 

【Android】Service 生命周期

作为一款多任务操作系统,如果不能运行后台服务,显然说不过去,Android 当然提供了运行后台程序的方法。而且非常简单易用,只不过有一些小问题需要注意,这个主题分为两部分,*部分是如何实现一个Service以及他的生命周期,第二部分是对于一个个后台服务应该注意的事项。我们开始吧!

创建服务类

所谓的服务,在Android里被称做 Service,只要继承 android.app.Service 这个抽象类,并且实现其中几个方法就可以了。

里边必须实现的一个方法是 onBind(Intent intent) ,他具体是做什么的我们下边讲。还有两个重要的回调函数需要覆盖,onCreate() 和 onDestroy()。跟 Actitivty 类似,在创建和销毁 Service 时回调这两个函数,达到初始化或退出前保存状态。

服务的生命周期

有了 Service 类我们如何启动他呢,有两种方法:

Context.startService()
Context.bindService()
在同一个应用任何地方调用 startService() 方法就能启动 Service 了,然后系统会回调 Service 类的 onCreate() 以及 onStart() 方法。这样启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。

另外一种 bindService() 方法的意思是,把这个 Service 和调用 Service 的客户类绑起来,如果调用这个客户类被销毁,Service 也会被销毁。用这个方法的一个好处是,bindService() 方法执行后 Service 会回调上边提到的 onBind() 方发,你可以从这里返回一个实现了 IBind 接口的类,在客户端操作这个类就能和这个服务通信了,比如得到 Service 运行的状态或其他操作。如果 Service 还没有运行,使用这个方法启动 Service 就会 onCreate() 方法而不会调用 onStart()。

与 Service 通信并且让它持续运行

如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService() 。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。

提高 Service 优先级

Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保 证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

有一个方法可以给你更清晰的演示,进入 $SDK/tools 运行命令

返回的一大堆东西,观察 oom_adj 的值,如果是大于 8 一般就是属于 backgroud 随时可能被干掉,数值越小证明优先级越高,被干掉的时间越晚。你看phone的程序是 -12 说明电话就是电话,其他什么都干了了,也的能接电话对吧。另外还有一个 -100 的,更邪乎因为是 system 如果他也完蛋了,你得系统也就挂了,嘿嘿。

用其他方式启动 Service

其实不光能从 Activity 中启动 Service ,还有一个很有用的方法是接收系统的广播,这就要用到 Receiver 。在 Mainfest 文件中配置你得 Receiver 能接收什么样的广播消息,那么即使你得程序没有显示给用户,你的 Service 也能启动。你要做的就是继承 android.content.BroadcastReceiver ,然后实现 onReceive(Context context, Intent intent) 方法,就可以启动你得 Service 了。这里不能 bindService 因为一个 Receiver 是一个短暂存在的对象,所以 bind 是没有什么意义的。

资源消耗

大家都说 G1 的电池太不抗用,这个问题其实我看来跟多是软件的问题。1150毫安的电池不算大,但也不算小了,考虑到 500mhz 的 CPU 还是非常耗电的。因为一个 Service 要长时间后台运行,所以如果你得 Service 太过于消耗资源那电池更用不了多久了。

对于这个问题我有一点点考虑,和大家分享一下。因为一般 Service 都会启动另外的线程不断循环作一些操作,循环频率不易太高。也不要做太过于耗费资源的操作,特别是CPU资源,因为后台 Service 用户看不到,会比较莫名奇妙。具体可以结合 top 以及 logcat 监测使用情况。LOG中如果虚拟机频繁的 GC 应该也说明程序还有很大改进的余地。因为GC 也是很耗费CPU的。可能这些不光 Service 应该注意,只要是移动设备都应该考虑,才能给你的用户*佳的体验。

【Android】短信应用——短信信息实时获取

我们知道,只需通过代码就可以读到收件箱中的短信,发件箱中的短信;但是却没办法在短信发来的瞬间获取;如果我们在短信发来的一瞬间能得到相应的信息内容,那么我们就可以依次来展开很多应用了——也就是通过短信去远程操作一部手机。

 

如果想实时获取,就需要调用receiver了,写一个监听类,这样我们就可以实时获取短息信息了。

 

预览图:%title插图%num

 

还是来看看代码吧。

首先,我们需要创建一个监听类SMSBroadcastReceiver,让他去继承BroadcastReceiver。

再来初始化一个常量ACTION,并赋短信相关参数值。

 

android.provider.Telephony.SMS_RECEIVED
接着创建onReceive方法。

然后用getAction去监听手机短信相关动态,利用StringBuffer来保存短信信息。

 

再然后主要代码了。

%title插图%num
代码中的SMSAddress为发送短信的号码,SMSContent为短信内容。

 

要想看到是否成功获取,*简单的方法就是把这两个参数打印出来。

1|System.out.println(“发送号码:” + SMSAddress + “\n” + “短信内容:”
2|                                         + SMSContent);

不过要把他们加入for循环中,因为当新信息发来时,SMSAddress和SMSContent将被替换。

因此如果要是做应用时,也是在for循环中判断的。

*后要记得在Manifest.xml中注册监听器。

 

<receiver android:name=”cn.etzmico.SMSBroadcastReceiver”>
<intent-filter>
<action android:name=”android.provider.Telephony.SMS_RECEIVED”></action>
</intent-filter>
</receiver>

 

同时要加上权限。

 

<uses-permission android:name=”android.permission.RECEIVE_SMS”></uses-permission>
这样,我们运行程序后,只要有短信接收,SMSAddress和SMSContent就会被赋值。

 

这里顺便补充一个知识点,关于Eclipse程序的。

相信很多初学者不知道,Eclipse自带一个发短信插件,可以实现给虚拟机发送短信。这样,我们在做短信应用的时候,就不用同时启动多台虚拟机了……

如何操作呢?方法如下。

1.点击菜单栏中的 Window 窗口。

2.找到哦啊其中的 Show View 目录。

3. 选择 Other…。

%title插图%num

然后我们发现会弹出一个窗口。

%title插图%num

4,为了便于操作,我们在弹出的窗口的搜索栏中,直接输入 Emulator Control。

%title插图%num

5.点击列表中的 Emulator Control,再点OK;或者直接双击。

 

这样就出现了一个窗口,其中有很多参数。

其他的以后有机会再做介绍,我们这次至用到其中4个。

%title插图%num

如图所示,我们只需要输入对应的参数,选择需要的类型,*后点发送就可以了。

 

PS:有的人奇怪为什么灰色,没法输入,没法选择,那是因为你没有选中模拟器。这个插件只能同时给一个模拟器发送消息。关于模拟器的选择,和调用Emulator Control的方法差不多,区别只是在输入Emulator Control的时候输入 Devices 就可以了。你当前选中哪个模拟器了,就会给哪个模拟器发送消息,不需要输入模拟器号码。

Android基于局域网的socket通信

*近写了一个关于局域网socket通信的demo,代码和ui都很low,但是功能实现了,所以贴出来记录一下

主要流程如下

服务端:启动服务–>显示ip–>等待接收–>显示消息–>收到回复

客户端:录入ip–>输入消息–>发送消息–>收到结果

如下图

选择服务端还是客户端

%title插图%num

 

显示服务端ip

%title插图%num

客户端输入ip发送消息

%title插图%num

服务端收到消息

%title插图%num

主要代码如下:
服务端逻辑

public class ServiceActivity extends AppCompatActivity {

private TextView tv_clear;
private TextView tv_showIP;
private TextView tv_ip;
private TextView tv_msg;
private ServerSocket mServerSocket;
private Socket mSocket;
private StringBuffer sb = new StringBuffer();

@SuppressLint(“HandlerLeak”)
public Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
Bundle data = msg.getData();
sb.append(data.getString(“msg”));
sb.append(“\n”);
tv_msg.setText(sb.toString());
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
initView();
setListener();

try {
mServerSocket = new ServerSocket(1989);
} catch (IOException e) {
e.printStackTrace();
}
//启动服务线程
SocketAcceptThread socketAcceptThread = new SocketAcceptThread();
socketAcceptThread.start();
}

private void initView() {
tv_clear = (TextView) findViewById(R.id.tv_clear);
tv_showIP = (TextView) findViewById(R.id.tv_showIP);
tv_ip = (TextView) findViewById(R.id.tv_ip);
tv_msg = (TextView) findViewById(R.id.tv_msg);
}

private void setListener() {

tv_clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sb.setLength(0);
tv_msg.setText(“”);
}
});
tv_showIP.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv_ip.setText(NetWorkUtil.getIPAddress(ServiceActivity.this));
}
});

}

class SocketAcceptThread extends Thread{
@Override
public void run() {
try {
//等待客户端的连接,Accept会阻塞,直到建立连接,
//所以需要放在子线程中运行。
mSocket = mServerSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.e(“info”, “run: ==============”+”accept error” );
return;
}
Log.e(“info”, “accept success==================”);
//启动消息接收线程
startReader(mSocket);
}
}

/**
* 从参数的Socket里获取*新的消息
*/
private void startReader(final Socket socket) {

new Thread(){
@Override
public void run() {
DataInputStream reader;
try {
// 获取读取流
reader = new DataInputStream(socket.getInputStream());
while (true) {
System.out.println(“*等待客户端输入*”);
// 读取数据
String msg = reader.readUTF();
System.out.println(“获取到客户端的信息:=” + msg);

//告知客户端消息收到
DataOutputStream writer = new DataOutputStream(mSocket.getOutputStream());
writer.writeUTF(“收到:” + msg); // 写一个UTF-8的信息

//发消息更新UI
Message message = new Message();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString(“msg”, msg);
message.setData(bundle);
handler.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}

@Override
protected void onDestroy() {
if (mServerSocket != null){
try {
mServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
服务端UI

<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
android:background=”#ffffff”
tools:context=”com.xiaoxiao9575.socketapplication.ServiceActivity”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”>
<TextView
android:id=”@+id/tv_showIP”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:text=”显示IP”
android:gravity=”center”
android:padding=”15dp”
android:background=”#dddddd”/>
<TextView
android:id=”@+id/tv_clear”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
android:layout_weight=”1″
android:text=”清除屏幕”
android:gravity=”center”
android:padding=”15dp”
android:background=”#dddddd”
android:layout_marginLeft=”5dp”/>
</LinearLayout>

<TextView
android:id=”@+id/tv_ip”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_margin=”10dp”/>

<TextView
android:id=”@+id/tv_msg”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginTop=”40dp”
android:layout_margin=”10dp”/>
</LinearLayout>
客户端逻辑

public class ClientActivity extends AppCompatActivity {

private EditText et_ip;
private EditText et_msg;
private TextView tv_send;
private TextView tv_confirm;

private Socket mSocket;
private OutputStream mOutStream;
private InputStream mInStream;
private SocketConnectThread socketConnectThread;
private StringBuffer sb = new StringBuffer();

@SuppressLint(“HandlerLeak”)
public Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
Bundle data = msg.getData();
sb.append(data.getString(“msg”));
sb.append(“\n”);
tv_msg.setText(sb.toString());
}
}
};
private TextView tv_msg;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
socketConnectThread = new SocketConnectThread();
initView();
setListener();
}

private void initView() {
et_ip = (EditText) findViewById(R.id.et_ip);
et_msg = (EditText) findViewById(R.id.et_msg);
tv_send = (TextView) findViewById(R.id.tv_send);
tv_confirm = (TextView) findViewById(R.id.tv_confirm);
tv_msg = (TextView) findViewById(R.id.tv_msg);
}

private void setListener() {
tv_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
send(et_msg.getText().toString());
}
});
tv_confirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
socketConnectThread.start();
}
});
}

class SocketConnectThread extends Thread{
public void run(){
Log.e(“info”, “run: ============线程启动” );
try {
//指定ip地址和端口号
mSocket = new Socket(et_ip.getText().toString(), 1989);
if(mSocket != null){
//获取输出流、输入流
mOutStream = mSocket.getOutputStream();
mInStream = mSocket.getInputStream();
}else {
Log.e(“info”, “run: =========scoket==null”);
}
} catch (Exception e) {
e.printStackTrace();
return;
}
Log.e(“info”,”connect success========================================”);
startReader(mSocket);
}

}

public void send(final String str) {
if (str.length() == 0){
return;
}
new Thread() {
@Override
public void run() {
try {
// socket.getInputStream()
DataOutputStream writer = new DataOutputStream(mSocket.getOutputStream());
writer.writeUTF(str); // 写一个UTF-8的信息
System.out.println(“发送消息”);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}

/**
* 从参数的Socket里获取*新的消息
*/
private void startReader(final Socket socket) {

new Thread(){
@Override
public void run() {
DataInputStream reader;
try {
// 获取读取流
reader = new DataInputStream(socket.getInputStream());
while (true) {
System.out.println(“*等待客户端输入*”);
// 读取数据
String msg = reader.readUTF();
System.out.println(“获取到客户端的信息:=” + msg);
Message message = new Message();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString(“msg”, msg);
message.setData(bundle);
handler.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
客户端UI

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:orientation=”vertical”
tools:context=”com.xiaoxiao9575.socketapplication.ClientActivity”>

<EditText
android:id=”@+id/et_ip”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:hint=”ip”
/>
<TextView
android:id=”@+id/tv_confirm”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”confirm”
android:padding=”20dp”
android:gravity=”center”
android:background=”#dddddd”/>
<EditText
android:id=”@+id/et_msg”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:hint=”msg”/>
<TextView
android:id=”@+id/tv_send”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:text=”send”
android:padding=”20dp”
android:gravity=”center”
android:background=”#dddddd”/>
<TextView
android:id=”@+id/tv_msg”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
/>
</LinearLayout>
获取ip代码

public static String getIPAddress(Context context) {
NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
try {
//Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}

} else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4地址
return ipAddress;
}
} else {
//当前无网络连接,请在设置中打开网络
}
return null;
}

/**
* 将得到的int类型的IP转换为String类型
*
* @param ip
* @return
*/
public static String intIP2StringIP(int ip) {
return (ip & 0xFF) + “.” +
((ip >> 8) & 0xFF) + “.” +
((ip >> 16) & 0xFF) + “.” +
(ip >> 24 & 0xFF);
}
权限

<!–允许应用程序改变网络状态–>
<uses-permission android:name=”android.permission.CHANGE_NETWORK_STATE”/>
<!–允许应用程序改变WIFI连接状态–>
<uses-permission android:name=”android.permission.CHANGE_WIFI_STATE”/>
<!–允许应用程序访问有关的网络信息–>
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE”/>
<!–允许应用程序访问WIFI网卡的网络信息–>
<uses-permission android:name=”android.permission.ACCESS_WIFI_STATE”/>
<!–允许应用程序完全使用网络–>
<uses-permission android:name=”android.permission.INTERNET”/>

理性分析,拼多多是怎么远程删除用户图片的?技术上是否可行?

19 条回复    2021-01-14 10:22:41 +08:00
HongJay
    1

HongJay   87 天前

安卓可以。但没必要。怀疑 bug
ArJun
    2

ArJun   87 天前

获取了权限应该是可以删除的,安卓的权限很霸道
codehz
    3

codehz   87 天前 via Android

(这就是为啥谷歌要在 android 11 限制读取 /sdcard,阻止访问 Android/data
(以及推了好几个版本也没推上的 scoped storage
xiaoliu926
    4

xiaoliu926   87 天前

android 获取相册读取存储权限,你手机上几乎啥都能远程删除
yukiww233
    5

yukiww233   87 天前

可以
不过感觉大概率是发送截图时本地压缩了一份,发完之后缓存就删了
tomari
    6

tomari   87 天前

给了权限就可以,但是个人认为没有这种奇怪的需求和吃力不讨好的必要。。我觉得跟用录音推荐商品一样吃力不讨好
testxss
    7

testxss   87 天前

这么高风险低价值的事情,做了有什么用呢?
ajaxfunction
    8

ajaxfunction   87 天前

就和 app 可以做到视频摄像头监控一个人吗?
肯定可以,比如微信 app,程序员把微信视频聊天功能里 代码搞成自动接听就完了,
但是这样子似乎毫无意义
ku9527
    9

ku9527   87 天前

@HongJay 你这意思是:我胖虎冤枉你大雄了 ^_^
westoy
    10

westoy   87 天前

@tomari

未必
就技术和实现上来说
录音推荐一是识别成本高&&耗电, 二是准确度低, 说白了就是费力而且不划算啊
但是某个 app 用户投诉客服被套路, 客服给用户标识一个 flag, app 端远程接收一个动态指令和模型, 扫一两天内的小图片, 然后根据模式识别是不是针对性的截图, 是就删掉, 技术上并不是不可行的, 而且开销也不大, 好处也是实打实的

Nillouise
    11

Nillouise   87 天前

@westoy 录音识别应该有专门的低耗电芯片做出来,faceID 用的好像就是专门的芯片识别人脸,没理由录音做不出来。
xiyuesaves
    12

xiyuesaves   87 天前

理论上只要你给了存储权限,app 就能访问整个存储空间了,miui 到是在升到安卓 11 之后单独吧相册权限拎出来需要单独授权了
telami
    13

telami   87 天前

帮别人砍了一刀,就下载了拼多多,然后有一天突然发现,我相册里面的照片,被像发朋友圈一样,展示在了拼多多里面,真是太恶心了
westoy
    14

westoy   87 天前

@Nillouise

那个是少量的触发唤醒词, 实时分析语义不可能放本地的

longxk
    15

longxk   87 天前   ❤️ 1

按拼多多的说法,只是删除了缓存图片,我觉得没有问题。
只是缓存图片没有做隐藏处理,被图库索引到了。
然后华为和 VIVO 的系统会拦截 APP 对图库文件的删除操作,导致了所谓的远程删除提示。
那个视频只是提到了拼多多有被拦截的删除操作,连时机都没有说明,删除的是否是他的截图也没法证明。
julyclyde
    16

julyclyde   87 天前

说他偷照片都比删照片可信
我觉得他们的解释(图片处理过程中的临时文件)说得过去
pwn
    17

pwn   87 天前

首先技术上完全可行,安卓的权限就这么乱来,没有区分,有点期待 MIUI 12.5 给出的解决方案。
其次,拼多多的解释是说删除的是缓存文件,而实际上被删除的是截图文件,截图文件的保存位置跟缓存文件的保存位置完全不一样好吧。用户调用拼多多里面的拍照功能得到的图片,放在缓存合理,删掉也可以,是一个程序能做的,但是跑去删系统目录(一般是 DCIM )下面的文件,就说不过去了。
但神奇的是,几乎所有人都忽视了这个区别,以为拼多多的解释合理。。
wsseo
    18

wsseo   86 天前

@pwn 拼多多的说法不是截屏,是通过 pdd 调用消息拍摄的图片。
cw2k13as
    19

cw2k13as   86 天前

@Nillouise 有的,那些官方支持语音唤醒的都用这个芯片

在 Android 虚拟设备上运行程序

在 Android 虚拟设备上运行程序

在通过上述方法创建 Android 项目后,就可以直接运行查看效果了。

1)左键选中项目,点击“Run”的图标(红色类似播放的按钮),选择 Android Application。

Run As Android Application

2)如果还没有建立 Android 虚拟程序(AVD),并且也没有连接待调试的 Android 真机设备,那么 Eclipse 将会弹出提示错误的对话框:没有可匹配的运行目标,是否需要添加一台新的 Android 虚拟设备?选择“Yes”。

Android AVD Error

3)在 Android Virtual Device (AVD)Manager,即 AVD 管理器创建 AVD,在 Android Virtual Devices 选项卡中点击“Create”。

Create AVD

3)接下来对待创建的 AVD 进行配置。

  • AVD Name (AVD 名称)
  • Device (设备型号,包含尺寸、分辨率、机型)
  • Target (目标运行平台,Android 系统版本)
  • CPU/ABI (CPU 的应用程序二进制接口)
  • Skin (AVD 的皮肤)
  • Front Camera (前置摄像头)
  • Back Camera (后置摄像头)
  • Memory Options (运行内存选项)
  • Internal Storage (内置存储空间)
  • SD Card (SD 卡配置)
  • Emulation Options (仿真器选项)

Device 一项,如果是希望在手机上模拟运行,选择“4” WVGA(Nexus S)(480 × 800: hdpi)”比较适合在电脑显示;Target 则可根据 Android SDK 所下载的平台支持来选择;CPU/ABI 首选 ARM;Skin 选择“skin with dynamic hardware controls”;Front Camera 和 Back Camera 在电脑上运行基本没有必要,选择“None”;Memory Options 的 RAM,条件允许的话选择 768 *为合适,因为在Windows上,模拟超过 768M 内存时可能在系统启动时失败,VM Heap 32;Internal Storage 和 SD Card 设定 200 和 512 就好了,点击 OK 完成创建。

参考:https://www.cnblogs.com/yangmingyu/p/6928084.html

Edit AVD

4)创建好的 AVD 会在管理器中显示,选中新建的模拟器,点击“Start”并“Launch”。

Launch

5)Eclipse 弹出提示询问是否要在 Logcat 自动监控 ADT 时,可选择“监控并显示 logcat 视图以便显示此工作空间中优先级高于 error 的程序信息”,方便程序调试,确定后 Eclipse 控制台将会出现一个 Logcat 窗体。

Auto Monitor Logcat

5)耐心等待 AVD 启动,Android 程序自动运行,首栏显示 app 名称,并在界面布局中显示“Hello world!”,说明项目已成功运行。

AVD 运行效果

参考:https://blog.csdn.net/yym373872996/article/details/50757658

遇到问题:

1、创建AVD错误 no cpu/abi system image available for this target

http://blog.sina.com.cn/s/blog_54aee4a80102vfvt.html

2、http://dl-ssl.google.com/android上不去(无法连接Google下载API)

https://blog.csdn.net/wangcheng52/article/details/72854342

3、解决 Android :Your project contains error(s),please fix them before running your application.错误

https://yq.aliyun.com/articles/333940

Error parsing D:\sdkforas\android-sdk-windows\system-images\android-24\android-wear\x86\devices.xml

https://blog.csdn.net/yang5726685/article/details/53067319

https://www.jianshu.com/p/1652505c87b7

创建Android工程的目录结构

工程目录结构

%title插图%num

1. Src:该目录中存放的是该项目的源代码,这个目录包含了你即将创建的Java源代码文件,这个目录里的文件是根据package结构管理的,它与普通java项目中的/src目录很相似。

2.Gen:自动生成的文件目录。该目录下的文件全部都是ADT自动生成的,一般并不需要去修改,实际上该目录下只定义了一个R.java文件,该文件相当于项目的字典,为项目中用户界面、字符串、图片等资源都会在该类中创建其惟一的ID,当项目中使用这些资源时,会通过该ID得到资源的引用。

3.Android {版本号}:这个目录包含了项目需要的库文件(Jar文件),这和普通Java项目中的/lib目录很相似。同时其中还包含项目打包时需要的META-INF目录.

4.Android Private Libraries: 所有的第三方JAR包引入都被放入了Android Private Libraries中

5.assets:资源路径,不会在R文件注册。该目录用于存放项目相关的资源文件,这个目录和res包含的xml文件差不多,也是应用中引用到的一些外部资源。但主要区别在于这些资源是以原始格式保存,且只能用编程方式读取。例如文本文件,视频文件,MP3音频等媒体文件。

6.bin:编译生成目录。二进制文件,包括class、资源文件、dex、apk等

7.res:该目录用于存放应用程序中经常使用的资源文件,其中包括图片、布局文件以及参数描述文件等,其中包括多个目录

(1)其中以drawable开头的三个文件夹用于存储.png、.9.png、.jpg等图片资源(.9.png是Android特有的图片格式,可以根据情况进行拉伸,达到不变形的效果),他们的分变率从高到低,如果你打算在android应用中包含一个图片或者图标,就应该把它们放在这个目录。

(2)layout文件夹存放的是应用程序的布局文件,这些layout是以xml形式保存的,关于layout的进一步信息,你可以参考android文档中的UI layout。

(3)raw用于存放应用程序所用到的声音等资源。raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;相比较assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。

(4)values 这个目录也包含了一些xml文件,但主要是应用中要引用的key-value对。这些XML文件声明了数组(Array)、颜色(color)、度量(Dimension)、字符串。之所以把这些东西分别放在单独的xml文件中主要是考虑到这些值能够在不更改源代码的情况下用于多语言环境。例如,根据用户语言的不同应用程序中的信息可以有多种语言版本。

8.AndroidManifest.xml:安卓清单文件 这个XML文件包含了android应用中的元信息,是每个android项目中的重要文件。在软件安装的时候被读取 ,Android中的四大组件(Activity、ContentProvider、BroadcastReceiver、Service)都需要在该文件中,以及运行这个android应用程序需要的用户权限列表,例如:电话、短信、互联网、访问SD卡,同时也详细描述了android应用的项目结构。

9. proguard-project.txt:代码混淆相关文件

10.project.properties:工程属性的配置文件,配置编译的版本等。ADT14 以后, project.properties和default.properties合并成了project.properties。供Eclipse使用,读取该项目使用Android版本号

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速