标签归档:代码示范

android 设置EditText光标位置

Android中有很多可编辑的弹出框,其中有些是让我们来修改其中的字符,这时光标位置定位在哪里呢?
刚刚解了一个bug是关于这个光标的位置的,似乎Android原生中这种情况是把光标定位到字符串的最前面。需求是将光标定位到字符的最后面。
修改的地方是TextView这个控件,因为EditText也是继承了TextView。在setText方法中有:

1  private void setText(CharSequence text, BufferType type,
2                          boolean notifyBefore, int oldlen) {
3 ……
4         if (text instanceof Spannable) {
5             Spannable sp = (Spannable) text;
6
7             ……
8             if (mMovement != null) {
9                 mMovement.initialize(this, (Spannable) text);
10         //文本是不是Editable的。
11         if(this instanceof Editable)
12                      //设定光标位置
13                      Selection.setSelection((Spannable)text, text.length());
14
15                ……
16     }

从红色代码中可以看出,google是要光标处在缺省文本的末端,但是,log发现 (this instanceof Editable)非真,也就是说Selection.setSelection((Spannable)text, text.length());并不会被执行。

1    Log.d(“TextView”, “(type == BufferType.EDITABLE)=”+(type == BufferType.EDITABLE));
2    if(type == BufferType.EDITABLE){
3          Log.d(“TextView”,”Format text.Set cursor to the end “);
4          Selection.setSelection((Spannable)text, text.length());
5    }

这个样修改后即可。

 

在编写应用的时候,如果我们要将光标定位到某个位置,可以采用下面的方法:

1 CharSequence text = editText.getText();
2 //Debug.asserts(text instanceof Spannable);
3 if (text instanceof Spannable) {
4     Spannable spanText = (Spannable)text;
5     Selection.setSelection(spanText, text.length());
6 }

其中红色标记的代码为你想要设置的位置,此处是设置到文本末尾。

android 主线程和子线程之间通过handler关联消息传递

从主线程发送消息到子线程(准确地说应该是非UI线程)

复制代码
 package com.zhuozhuo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class LooperThreadActivity extends Activity{
/** Called when the activity is first created. */

private final int MSG_HELLO = 0;
private Handler mHandler;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new CustomThread().start();//新建并启动CustomThread实例

findViewById(R.id.send_btn).setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {//点击界面时发送消息
                String str = “hello”;
Log.d(“Test”, “MainThread is ready to send msg:” + str);
mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//发送消息到CustomThread实例

}
});

}

class CustomThread extends Thread {
@Override
public void run() {
//建立消息循环的步骤
            Looper.prepare();//1、初始化Looper
            mHandler = new Handler(){//2、绑定handler到CustomThread实例的Looper对象
                public void handleMessage (Message msg) {//3、定义处理消息的方法
                    switch(msg.what) {
case MSG_HELLO:
Log.d(“Test”, “CustomThread receive msg:” + (String) msg.obj);
}
}
};
Looper.loop();//4、启动消息循环
        }
}
}

复制代码

从非UI线程传递消息到UI线程(界面主线程),因为主界面已经有MessageQueue,所以可以直接获取消息处理消息。而上面由主线程向非UI线程中处理消息的时候,非UI线程需要先添加消息队列,然后处理消息循环。

复制代码

public class ThreadHandlerActivity extends Activity {
/** Called when the activity is first created. */

private static final int MSG_SUCCESS = 0;//获取图片成功的标识
    private static final int MSG_FAILURE = 1;//获取图片失败的标识

private ImageView mImageView;
private Button mButton;

private Thread mThread;

private Handler mHandler = new Handler() {
public void handleMessage (Message msg) {//此方法在ui线程运行
            switch(msg.what) {
case MSG_SUCCESS:
mImageView.setImageBitmap((Bitmap) msg.obj);//imageview显示从网络获取到的logo
                Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_success), Toast.LENGTH_LONG).show();
break;

case MSG_FAILURE:
Toast.makeText(getApplication(), getApplication().getString(R.string.get_pic_failure), Toast.LENGTH_LONG).show();
break;
}
}
};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView= (ImageView) findViewById(R.id.imageView);//显示图片的ImageView
        mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if(mThread == null) {
mThread = new Thread(runnable);
mThread.start();//线程启动
                }
else {
Toast.makeText(getApplication(), getApplication().getString(R.string.thread_started), Toast.LENGTH_LONG).show();
}
}
});
}

Runnable runnable = new Runnable() {

@Override
public void run() {//run()在新的线程中运行
            HttpClient hc = new DefaultHttpClient();
HttpGet hg = new HttpGet(“http://csdnimg.cn/www/images/csdnindex_logo.gif”);//获取csdn的logo
            final Bitmap bm;
try {
HttpResponse hr = hc.execute(hg);
bm = BitmapFactory.decodeStream(hr.getEntity().getContent());
catch (Exception e) {
mHandler.obtainMessage(MSG_FAILURE).sendToTarget();//获取图片失败
                return;
}
mHandler.obtainMessage(MSG_SUCCESS,bm).sendToTarget();//获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象

//            mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素

//            mImageView.post(new Runnable() {//另外一种更简洁的发送消息给ui线程的方法。
//
//                @Override
//                public void run() {//run()方法会在ui线程执行
//                    mImageView.setImageBitmap(bm);
//                }
//            });
        }
};

复制代码

}

作者:Jackhuclan
出处:http://jackhuclan.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

利用Android的 IntentService完成后台异步任务

初学android比较纠结的一点就是service和activity的交互,在service中不能进行耗时的操作,否则会阻塞UI进程,我们可以在service中new一个Thread出来进行耗时的操作,但毕竟有点麻烦。所以android还提供了IntentService类来帮助解决这个问题。

IntentService是继承至Service子类,用于处理异步的请求,你可以通过startService(Intent)来发送你需要的请求。在IntentService中使用了一个ServiceHandler(继承至Handler)来处理接收到的请求。对于每个异步的startService请求,IntentService会处理完成一个之后再处理第二个。每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程。

IntentService的特点如下:

(1)它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。

(2)创建了一个工作队列,来逐个发送intent给onHandleIntent()。

(3)不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。

(4)默认实现的onBind()返回null

(5)默认实现的onStartCommand()的目的是将intent插入到工作队列中。

IntentService是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.意思是Intent的生命周期跟你的处理的任务是一致的.所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出.

使用方法也十分简单,所需要做的就是实现 onHandleIntent() 方法,在该方法内实现你想进行的操作。另外,继承IntentService时,你必须提供一个无参构造函数,且在该构造函数内,你需要调用父类的构造函数

启动服务:

findViewById(R.id.intentService).setOnClickListener(

new OnClickListener() {

@Override

public void onClick(View v) {

startService( new Intent(Main.this,

IntentServiceExm. class));

}

});

IntentService服务:

public class IntentServiceExm extends IntentService {

public IntentServiceExm() {

super(“IntentServiceExm” );

}

@Override

protected void onHandleIntent(Intent intent) {

try {

// 这里模拟耗时操作,睡眠5秒

Thread.sleep(5000);

Log. i(“IntentService”, “in IntentService” );

catch (InterruptedException e) {

e.printStackTrace();

}

}

}

在此特别需要注意的是IntentService是线性处理请求的。如果有多个Intent请求执行,则会被放在工作队列中,依次等待,顺序执行。所以若是想在Service中让多个线程并发的话,就得另想法子,比如本文开头所说的在service中新建多个Thread来解决。

java 多线程 之 CountDownLatch 代码示例

CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
这个概念和unix中的屏障(barrier)很相似,可能底层实现就是barrier。

屏障允许任意数量的线程等待,直到所有的线程完成处理工作,而线程不需要退出。所有线程达到屏障后可以接着工作。

#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *restrict barrier,const pthread_barrierattr_t *restirct attr, unsigned int count);
int pthread_barrier_destroy(pthread_barrier_t *barrier);

ccount指定屏障计数。
#include <pthread.h>
int pthread_barrier_wait(pthread_barrier_t *barrier);
调用pthread_barrier_wait的线程在屏障计数未满足条件时,进入休眠状态。如果满足屏障计数,所有的线程都被唤醒。
    其中一个线程会返回PTHREAD_BARRIER_SERIAL_THREAD,剩下的线程看到的返回值是0,这可以控制其中一个线程
    作为主线程,它可以工作在其他线程已经完成的工作结果上。

CountDownLatch的主要方法

 public CountDownLatch(int count);

 public void countDown();

 public void await() throws InterruptedException

构造方法参数指定了计数的次数

countDown方法,当前线程调用此方法,则计数减一

awaint方法,调用此方法会一直阻塞当前线程,直到计时器的值为0

例子

public class CountDownLatchDemo {  
    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch latch=new CountDownLatch(2);//两个工人的协作  
        Worker worker1=new Worker("zhang san", 5000, latch);  
        Worker worker2=new Worker("li si", 8000, latch);  
        worker1.start();//  
        worker2.start();//  
        latch.await();//等待所有工人完成工作  
        System.out.println("all work done at "+sdf.format(new Date()));  
    }  
      
      
    static class Worker extends Thread{  
        String workerName;   
        int workTime;  
        CountDownLatch latch;  
        public Worker(String workerName ,int workTime ,CountDownLatch latch){  
             this.workerName=workerName;  
             this.workTime=workTime;  
             this.latch=latch;  
        }  
        public void run(){  
            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));  
            doWork();//工作了  
            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));  
            latch.countDown();//工人完成工作,计数器减一  
  
        }  
          
        private void doWork(){  
            try {  
                Thread.sleep(workTime);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
       
}  

输出:

Worker zhang san do work begin at 2011-04-14 11:05:11
Worker li si do work begin at 2011-04-14 11:05:11
Worker zhang san do work complete at 2011-04-14 11:05:16
Worker li si do work complete at 2011-04-14 11:05:19
all work done at 2011-04-14 11:05:19

java多线程之Callable代码示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/** *//**
 * Callable 和 Future接口
 * Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
 * Callable和Runnable有几点不同:
 * (1)Callable规定的方法是call(),而Runnable规定的方法是run().
 * (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
 * (3)call()方法可抛出异常,而run()方法是不能抛出异常的。
 * (4)运行Callable任务可拿到一个Future对象,
 * Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
 * 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
 */
public class CallableAndFuture {

/** *//**
     * 自定义一个任务类,实现Callable接口
     */
    public static class MyCallableClass implements Callable{
        // 标志位
        private int flag = 0;
        public MyCallableClass(int flag){
            this.flag = flag;
        }
        public String call() throws Exception{
            if (this.flag == 0){
                // 如果flag的值为0,则立即返回
                return "flag = 0";
            }
            if (this.flag == 1){
                // 如果flag的值为1,做一个无限循环
                try {
                    while (true) {
                        System.out.println("looping.");
                        Thread.sleep(2000);
                    }
                } catch (InterruptedException e) {
                    System.out.println("Interrupted");
                }
                return "false";
            } else {
                // falg不为0或者1,则抛出异常
                throw new Exception("Bad flag value!");
            }
        }
    }

public static void main(String[] args) {
        // 定义3个Callable类型的任务
        MyCallableClass task1 = new MyCallableClass(0);
        MyCallableClass task2 = new MyCallableClass(1);
        MyCallableClass task3 = new MyCallableClass(2);

// 创建一个执行任务的服务
        ExecutorService es = Executors.newFixedThreadPool(3);
        try {
            // 提交并执行任务,任务启动时返回了一个 Future对象,
            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
            Future future1 = es.submit(task1);
            // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
            System.out.println("task1: " + future1.get());

Future future2 = es.submit(task2);
            // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
            Thread.sleep(5000);
            System.out.println("task2 cancel: " + future2.cancel(true));

// 获取第三个任务的输出,因为执行第三个任务会引起异常
            // 所以下面的语句将引起异常的抛出
            Future future3 = es.submit(task3);
            System.out.println("task3: " + future3.get());
        } catch (Exception e){
            System.out.println(e.toString());
        }
        // 停止任务执行服务
        es.shutdownNow();
    }
}

Android 中 Socket的简单用法

Socket通常也称做”套接字“,用于描述IP地址和端口,废话不多说,它就是网络通信过程中端点的抽象表示。值得一提的是,Java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用起来很方便!

下面将首先创建一个SocketServer的类作为服务端如下,该服务端实现了多线程机制,可以在特定端口处监听多个客户请求,一旦有客户请求,Server总是会创建一个服务纯种来服务新来的客户,而自己继续监听。程序中accept()是一个阻塞函数,所谓阻塞性方法就是说该方法被调用后将等待客户的请求,直到有一个客户启动并请求连接到相同的端口,然后accept()返回一个对应于客户的Socket。这时,客户方和服务方都建立了用于通信的Socket,接下来就是由各个Socket分别打开各自的输入、输出流。

SocketServer类,服务器实现:

package HA.Socket;

import java.io.*;
import java.net.*;

 public class SocketServer {
    
    ServerSocket sever;
    
    public SocketServer(int port){
        try{
            sever = new ServerSocket(port);
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    
    public void beginListen(){
        while(true){
            try{
                final Socket socket = sever.accept();
                
                new Thread(new Runnable(){
                    public void run(){
                        BufferedReader in;
                        try{
                            in = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
                            PrintWriter out = new PrintWriter(socket.getOutputStream());
                            while (!socket.isClosed()){
                                String str;
                                str = in.readLine();
                                out.println("Hello!world!! " + str);
                                out.flush();
                                if (str == null || str.equals("end"))
                                    break;
                                System.out.println(str);
                            }
                            socket.close();
                        }catch(IOException e){
                            e.printStackTrace();
                        }
                    }
                }).start();
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}

SocketClient类,客户端实现:

package HA.Socket;

import java.io.*;
import java.net.*;

 public class SocketClient {
    static Socket client;
    
    public SocketClient(String site, int port){
        try{
            client = new Socket(site,port);
            System.out.println("Client is created! site:"+site+" port:"+port);
        }catch (UnknownHostException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    
    public String sendMsg(String msg){
        try{
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            PrintWriter out = new PrintWriter(client.getOutputStream());
            out.println(msg);
            out.flush();
            return in.readLine();
        }catch(IOException e){
            e.printStackTrace();
        }
        return "";
    }
    public void closeSocket(){
        try{
            client.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception{
        
    }

}

接下来就是来测试Socket通信了!
先运行TestSocketServer类,打开服务端,在12345端口处监听!

package HA.Socket;

 public class TestSocketServer {
    
    public static void main(String[] argvs){
        SocketServer server = new SocketServer(12345);
        server.beginListen();
    }
}

再运行TestSocketClient类:

package HA.Socket;

 public class TestSocketClient {

    public static void main(String[] args){
        
        SocketClient client = new SocketClient("127.0.0.1",12345);
        System.out.println(client.sendMsg("nimei1"));
        client.closeSocket();
        
        SocketClient client1 = new SocketClient("127.0.0.1",12345);
        System.out.println(client1.sendMsg("nimei1111"));
        client1.closeSocket();
        
        SocketClient client11 = new SocketClient("127.0.0.1",12345);
        System.out.println(client11.sendMsg("nimei11111111"));
        client11.closeSocket();
        
        SocketClient client111 = new SocketClient("127.0.0.1",12345);
        System.out.println(client111.sendMsg("nimei11111111111111111"));
        client111.closeSocket();
        
    }
}

输出结果如下:

服务端:

Client is created! site:127.0.0.1 port:12345
Hello!world!! nimei1
Client is created! site:127.0.0.1 port:12345
Hello!world!! nimei1111
Client is created! site:127.0.0.1 port:12345
Hello!world!! nimei11111111
Client is created! site:127.0.0.1 port:12345
Hello!world!! nimei11111111111111111

客户端:

nimei1
nimei1111
nimei11111111
nimei11111111111111111

Android中webview和js之间的交互

 

1.android中利用webview调用网页上的js代码。

Android 中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为true,,然后通过loadUrl就可以直接进行调用,如下所示:

mWebView.getSettings().setJavaScriptEnabled(true);

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

2. 网页上调用android中java代码的方法

在网页中调用java代码,需要在webview控件中添加javascriptInterface。如下所示:

mWebView.addJavascriptInterface(new Object() {

            @JavascriptInterface

            public void clickOnAndroid() {

                mHandler.post(new Runnable() {

                    public void run() {

                        Toast.makeText(Test.this, “测试调用java”, Toast.LENGTH_LONG).show();

                    }

                });

            }

        }, “demo”);

需要特别注意的是,在 JELLY_BEAN_MR1 之后,只有 public 且添加了 @JavascriptInterface 注解的方法才能被调用。这也是为了安全考虑。毕竟页面可以直接操作 Native Application 有点太可怕了。(没加注解,导致反射无效,查了好久也是醉了)

在网页中,只需要像调用js方法一样,进行调用就可以

<div id=’b’><a onclick=”window.demo.clickOnAndroid()”>b.c</a></div>

3. Java代码调用js并传参

首先需要带参数的js函数,如function test(str),然后只需在调用js时传入参数即可,如下所示:

mWebView.loadUrl(“javascript:test(‘aa’)”);

4.Js中调用java函数并传参

首先一样需要带参数的函数形式,但需注意此处的参数需要final类型,即得到以后不可修改,如果需要修改其中的值,可以先设置中间变量,然后进行修改。如下所示:

mWebView.addJavascriptInterface(new Object() {

            @JavascriptInterface

            public void clickOnAndroid(final int i) {

                mHandler.post(new Runnable() {

                    public void run() {

                                int j = i;

                                j++;

Toast.makeText(Test.this, “测试调用java” + String.valueOf(j), Toast.LENGTH_LONG).show();

                    }

                });

            }

        }, “demo”);

然后在html页面中,利用如下代码<div id=’b’><a onclick=”window.demo.clickOnAndroid(2)”>b.c</a></div>,

即可实现调用。

IMPORTANT

  • Javascript 和 Java 的交互是在子线程中的,必须注意线程安全
  • Java 对象中的成员方法不再能直接访问
  • For applications targeted to API level LOLLIPOP and above, methods of injected Java objects are enumerable from JavaScript. (目前还不知道官方文档中的这句话代表了什么)
  • 特别需要注意 Android 各个版本对 WebView 的要求导致的。越高的版本对 WebView 的限制更加严格。

References

http://developer.android.com/reference/android/webkit/WebView.html

Android Service生命周期及用法!

Service概念及用途:

Android中的服务,它与Activity不同,它是不能与用户交互的,不能自己启动的,运行在后台的程序,如果我们退出应用时,Service进程并没有结束,它仍然在后台运行,那
我们什么时候会用到Service呢?比如我们播放音乐的时候,有可能想边听音乐边干些其他事情,当我们退出播放音乐的应用,如果不用Service,我
们就听不到歌了,所以这时候就得用到Service了,又比如当我们一个应用的数据是通过网络获取的,不同时间(一段时间)的数据是不同的这时候我们可以
用Service在后台定时更新,而不用每打开应用的时候在去获取。

Service生命周期
:

Android Service的生命周期并不像Activity那么复杂,它只继承了onCreate(),onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用了onCreate(),onStart()这两个方法,当停止Service时,则执行onDestroy()方法,这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法,具体的可以看下面的实例。

Service与Activity通信:
Service后端的数据最终还是要呈现在前端Activity之上的,因为启动Service时,系统会重新开启一个新的进程,这就涉及到不同进程间通信的问题了(AIDL)这一节我不作过多描述,当我们想获取启动的Service实例时,我们可以用到bindService和onBindService方法,它们分别执行了Service中IBinder()和onUnbind()方法。

 

为了让大家 更容易理解,我写了一个简单的Demo,大家可以模仿着我,一步一步的来。

 

第一步:新建一个Android工程,我这里命名为ServiceDemo.

第二步:修改main.xml代码,我这里增加了四个按钮,代码如下:

 

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
  3.     android:orientation=“vertical”
  4.     android:layout_width=“fill_parent”
  5.     android:layout_height=“fill_parent”
  6.     >
  7.     <TextView
  8.         android:id=“@+id/text”
  9.         android:layout_width=“fill_parent”
  10.         android:layout_height=“wrap_content”
  11.         android:text=“@string/hello”
  12.         />
  13.     <Button
  14.         android:id=“@+id/startservice”
  15.         android:layout_width=“fill_parent”
  16.         android:layout_height=“wrap_content”
  17.         android:text=“startService”
  18.     />
  19.     <Button
  20.         android:id=“@+id/stopservice”
  21.         android:layout_width=“fill_parent”
  22.         android:layout_height=“wrap_content”
  23.         android:text=“stopService”
  24.     />
  25.     <Button
  26.         android:id=“@+id/bindservice”
  27.         android:layout_width=“fill_parent”
  28.         android:layout_height=“wrap_content”
  29.         android:text=“bindService”
  30.     />
  31.     <Button
  32.         android:id=“@+id/unbindservice”
  33.         android:layout_width=“fill_parent”
  34.         android:layout_height=“wrap_content”
  35.         android:text=“unbindService”
  36.     />
  37. </LinearLayout>

第三步:新建一个Service,命名为MyService.java代码如下:

 

  1. package com.tutor.servicedemo;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.Binder;
  5. import android.os.IBinder;
  6. import android.text.format.Time;
  7. import android.util.Log;
  8. public class MyService extends Service {
  9.     //定义个一个Tag标签
  10.     private static final String TAG = “MyService”;
  11.     //这里定义吧一个Binder类,用在onBind()有方法里,这样Activity那边可以获取到
  12.     private MyBinder mBinder = new MyBinder();
  13.     @Override
  14.     public IBinder onBind(Intent intent) {
  15.         Log.e(TAG, “start IBinder~~~”);
  16.         return mBinder;
  17.     }
  18.     @Override
  19.     public void onCreate() {
  20.         Log.e(TAG, “start onCreate~~~”);
  21.         super.onCreate();
  22.     }
  23.     @Override
  24.     public void onStart(Intent intent, int startId) {
  25.         Log.e(TAG, “start onStart~~~”);
  26.         super.onStart(intent, startId);
  27.     }
  28.     @Override
  29.     public void onDestroy() {
  30.         Log.e(TAG, “start onDestroy~~~”);
  31.         super.onDestroy();
  32.     }
  33.     @Override
  34.     public boolean onUnbind(Intent intent) {
  35.         Log.e(TAG, “start onUnbind~~~”);
  36.         return super.onUnbind(intent);
  37.     }
  38.     //这里我写了一个获取当前时间的函数,不过没有格式化就先这么着吧
  39.     public String getSystemTime(){
  40.         Time t = new Time();
  41.         t.setToNow();
  42.         return t.toString();
  43.     }
  44.     public class MyBinder extends Binder{
  45.         MyService getService()
  46.         {
  47.             return MyService.this;
  48.         }
  49.     }
  50. }

第四步:修改ServiceDemo.java,代码如下:

 

  1. package com.tutor.servicedemo;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.ServiceConnection;
  7. import android.os.Bundle;
  8. import android.os.IBinder;
  9. import android.view.View;
  10. import android.view.View.OnClickListener;
  11. import android.widget.Button;
  12. import android.widget.TextView;
  13. public class ServiceDemo extends Activity implements OnClickListener{
  14.     private MyService  mMyService;
  15.     private TextView mTextView;
  16.     private Button startServiceButton;
  17.     private Button stopServiceButton;
  18.     private Button bindServiceButton;
  19.     private Button unbindServiceButton;
  20.     private Context mContext;
  21.     //这里需要用到ServiceConnection在Context.bindService和context.unBindService()里用到
  22.     private ServiceConnection mServiceConnection = new ServiceConnection() {
  23.         //当我bindService时,让TextView显示MyService里getSystemTime()方法的返回值 
  24.         public void onServiceConnected(ComponentName name, IBinder service) {
  25.             // TODO Auto-generated method stub
  26.             mMyService = ((MyService.MyBinder)service).getService();
  27.             mTextView.setText(“I am frome Service :” + mMyService.getSystemTime());
  28.         }
  29.         public void onServiceDisconnected(ComponentName name) {
  30.             // TODO Auto-generated method stub
  31.         }
  32.     };
  33.     public void onCreate(Bundle savedInstanceState) {
  34.         super.onCreate(savedInstanceState);
  35.         setContentView(R.layout.main);
  36.         setupViews();
  37.     }
  38.     public void setupViews(){
  39.         mContext = ServiceDemo.this;
  40.         mTextView = (TextView)findViewById(R.id.text);
  41.         startServiceButton = (Button)findViewById(R.id.startservice);
  42.         stopServiceButton = (Button)findViewById(R.id.stopservice);
  43.         bindServiceButton = (Button)findViewById(R.id.bindservice);
  44.         unbindServiceButton = (Button)findViewById(R.id.unbindservice);
  45.         startServiceButton.setOnClickListener(this);
  46.         stopServiceButton.setOnClickListener(this);
  47.         bindServiceButton.setOnClickListener(this);
  48.         unbindServiceButton.setOnClickListener(this);
  49.     }
  50.     public void onClick(View v) {
  51.         // TODO Auto-generated method stub
  52.         if(v == startServiceButton){
  53.             Intent i  = new Intent();
  54.             i.setClass(ServiceDemo.this, MyService.class);
  55.             mContext.startService(i);
  56.         }else if(v == stopServiceButton){
  57.             Intent i  = new Intent();
  58.             i.setClass(ServiceDemo.this, MyService.class);
  59.             mContext.stopService(i);
  60.         }else if(v == bindServiceButton){
  61.             Intent i  = new Intent();
  62.             i.setClass(ServiceDemo.this, MyService.class);
  63.             mContext.bindService(i, mServiceConnection, BIND_AUTO_CREATE);
  64.         }else{
  65.             mContext.unbindService(mServiceConnection);
  66.         }
  67.     }
  68. }

第五步:修改AndroidManifest.xml代码(将我们新建的MyService注册进去如下代码第14行:)

  1. <?xml version=“1.0” encoding=“utf-8”?>
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”
  3.       package=“com.tutor.servicedemo”
  4.       android:versionCode=“1”
  5.       android:versionName=“1.0”>
  6.     <application android:icon=“@drawable/icon” android:label=“@string/app_name”>
  7.         <activity android:name=“.ServiceDemo”
  8.                   android:label=“@string/app_name”>
  9.             <intent-filter>
  10.                 <action android:name=“android.intent.action.MAIN” />
  11.                 <category android:name=“android.intent.category.LAUNCHER” />
  12.             </intent-filter>
  13.         </activity>
  14.         <service android:name=“.MyService” android:exported=“true”></service>
  15.     </application>
  16.     <uses-sdk android:minSdkVersion=“7” />
  17. </manifest>

 

第六步:执行上述工程,效果图如下:

点击startServie按钮时先后执行了Service中onCreate()->onStart()这两个方法,打开Logcat视窗效果如下图:

我们这时可以按HOME键进入Settings(设置)->Applications(应用)->Running Services(正在运行的服务)看一下我们新启动了一个服务,效果如下:

点击stopService按钮时,Service则执行了onDestroy()方法,效果图如下所示:

这时候我们再次点击startService按钮,然后点击bindService按钮(通常bindService都是bind已经启动的Service),我们看一下Service执行了IBinder()方法,以及TextView的值也有所变化了,如下两张图所示:

 

最后点击unbindService按钮,则Service执行了onUnbind()方法,如下图所示:

 

android declare-styleable中format 参考说明

我们在做项目的时候,由于Android自带的属性不能满足需求,android提供了自定义属性的方法,其中的format是做什么用的?以及如何使用它?下面列出一些常用的。

  • reference:参考某一资源ID。
  • color:颜色值。
  • boolean:布尔值。
  • dimension:尺寸值。
  • float:浮点值。
  • integer:整型值。
  • string:字符串。
  • fraction:百分数。
  • enum:枚举值。
  • flag:位或运算。

具体示例如下:

  • reference:参考某一资源ID。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "background" format = "reference" />
    </declare-styleable>
    

    2.属性使用:

    <ImageView
        android:layout_width = "42dip"
        android:layout_height = "42dip"
        android:background = "@drawable/图片ID"
    />
    
  • color:颜色值。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "textColor" format = "color" />
    </declare-styleable>
    

    2.属性使用:

    <TextView
        android:layout_width = "42dip"
        android:layout_height = "42dip"
        android:textColor = "#00FF00"
    />
    
  • boolean:布尔值。
    1. 属性定义:
    <declare-styleable name = "名称">
         <attr name = "focusable" format = "boolean" />
    </declare-styleable>
    

    2. 属性使用:

      <Button
      android:layout_width = "42dip"
      android:layout_height = "42dip"
      android:focusable = "true"
      />
    
  • dimension:尺寸值。
    1. 属性定义:
    <declare-styleable name = "名称">
        <attr name = "layout_width" format = "dimension" />
    </declare-styleable>
    

    2.属性使用:

    <Button
            android:layout_width = "42dip"
            android:layout_height = "42dip"
    />
    
  • float:浮点值。
    1. 属性定义:
    <declare-styleable name = "AlphaAnimation">
        <attr name = "fromAlpha" format = "float" />
        <attr name = "toAlpha" format = "float" />
    </declare-styleable>
    

    2.属性使用:

    <alpha 
        android:fromAlpha = "1.0"
        android:toAlpha = "0.7"
    />
    
  • integer:整型值。
    1. 属性定义:
    <declare-styleable name = "AnimatedRotateDrawable">
        <attr name = "visible" />
        <attr name = "frameDuration" format="integer" />
        <attr name = "framesCount" format="integer" />
        <attr name = "pivotX" />
        <attr name = "pivotY" />
        <attr name = "drawable" />
    </declare-styleable>
    

    2.属性使用:

    <animated-rotate
        xmlns:android = "http://schemas.android.com/apk/res/android" 
         android:drawable = "@drawable/图片ID" 
         android:pivotX = "50%" 
         android:pivotY = "50%" 
         android:framesCount = "12" 
         android:frameDuration = "100"
      />
    

     

  • string:字符串。
    1. 属性定义:
      <declare-styleable name = "MapView">
            <attr name = "apiKey" format = "string" />
      </declare-styleable>
    

    2.属性使用:

    <com.google.android.maps.MapView
            android:layout_width = "fill_parent"
         android:layout_height = "fill_parent"
         android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
     />
    
  • fraction:百分数。
    1. 属性定义:
      <declare-styleable name="RotateDrawable">
            <attr name = "visible" />
            <attr name = "fromDegrees" format = "float" />
            <attr name = "toDegrees" format = "float" />        <attr name = "pivotX" format = "fraction" />
            <attr name = "pivotY" format = "fraction" />
         <attr name = "drawable" />
      </declare-styleable>
    

    2.属性使用:

    <rotate
      xmlns:android = "http://schemas.android.com/apk/res/android"
        android:interpolator = "@anim/动画ID"
        android:fromDegrees = "0"
        android:toDegrees = "360"
        android:pivotX = "200%"
        android:pivotY = "300%"
        android:duration = "5000"
        android:repeatMode = "restart"
        android:repeatCount = "infinite"
        />
    
  • enum:枚举值。
    1. 属性定义:
    <declare-styleable name="名称">
        <attr name="orientation">
        <enum name="horizontal" value="0" />
        <enum name="vertical" value="1" />
        </attr>           
    </declare-styleable>
    

    2.属性使用:

    <LinearLayout
        xmlns:android = "http://schemas.android.com/apk/res/android"
        android:orientation = "vertical"
        android:layout_width = "fill_parent"
        android:layout_height = "fill_parent"
        >
    </LinearLayout>
    
  • flag:位或运算。
    1. 属性定义:
    <declare-styleable name="名称">
         <attr name="windowSoftInputMode">
         <flag name = "stateUnspecified" value = "0" />
         <flag name = "stateUnchanged" value = "1" />
         <flag name = "stateHidden" value = "2" />
         <flag name = "stateAlwaysHidden" value = "3" />
         <flag name = "stateVisible" value = "4" />
         <flag name = "stateAlwaysVisible" value = "5" />
         <flag name = "adjustUnspecified" value = "0x00" />
         <flag name = "adjustResize" value = "0x10" />
         <flag name = "adjustPan" value = "0x20" />
         <flag name = "adjustNothing" value = "0x30" />
         </attr>        
    </declare-styleable>
    

    2.属性使用:

    <activity
        android:name = ".StyleAndThemeActivity"
        android:label = "@string/app_name"
        android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
        <intent-filter>
            <action android:name = "android.intent.action.MAIN" />
            <category android:name = "android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

特别要注意:属性定义时可以指定多种类型值。
1. 属性定义:

<declare-styleable name = "名称">
    <attr name = "background" format = "reference|color" />
</declare-styleable>

2.属性使用:

<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID|#00FF00"
/>

 

 

java.util.ConcurrentModificationException异常(转)

 

案例

今天在写一个带缓存功能的访问代理程序时出现了java.util.ConcurrentModificationException异常,
因为该异常是非捕获型异常而且很少见,所以费了些时间才找到问题所在,原来在通过Iterator进行遍历的时候,如果直接对HashMap进行操作后,再继续用之前的Iterator进行遍历就会出现这个异常,表示其HashMap已经被修改。

源程序代码片段如下:caches为一个HashMap对象

String sameKeyPart = accesserClassName + "@" + methodName + "@" +
parameterKeyString + "@";
Iterator keys = caches.keySet().iterator();
String key = null;
while (keys.hasNext()){
    key = (String) keys.next();     
    if (key.startsWith(sameKeyPart)){
        caches.remove(key);  
    }
}

解决办法为通过其相应的Iterator进行删除就可以了,修改后代码片段如下:

String sameKeyPart = accesserClassName + "@" + methodName + "@" +
parameterKeyString + "@";
Iterator keys = caches.keySet().iterator();
String key = null;
while (keys.hasNext()) {
    key = (String) keys.next();     
    if (key.startsWith(sameKeyPart)) {       
        keys.remove();
    }
}

多线程操作时解决办法

撰写多线程代码时,你遇到过多少次下面的提示:
Exception in thread “main” java.util.ConcurrentModificationException

这个异常产生的原因有几个。

  1. 直接对集合调用删除操作而不是在枚举器上。
  2. 不同的线程试图对集合进行增删操作的时候。

这个解决办法的第一步就是同步代码,使得你在枚举的时候其它的线程不能增删记录。但是如果每个枚举过程要进行复杂的计算或者是数据库访问的一部分的话,这个同步就会导致可怕的后果。为了减少负面影响,可以拷贝一个只读的枚举器,去掉同步,然后采用下列代码所示的方法:

private List list;
public void add(Object obj) {
    synchronized(list) {
        list.add(obj);
  }
}

public void perform( ) {
    Iterator iterator = null;
    synchronized(list) {
        iterator = new CopiedIterator(list.iterator());
    }
    while(iterator.hasNext( )) {
    // perform resource or cpu hungry work
    }
 }

重要的是记住,CopiedIterator不是一个克隆,只是一个只读的拷贝,所以它并没有保持原有的全部功能。最重要的是,不能再调用CopiedIterator.remove方法了。CopiedIterator.remove的实现如下:

public class CopiedIterator implements Iterator {
    private Iterator iterator = null;
    public CopiedIterator(Iterator itr) {
        LinkedList list = new LinkedList();
        while(itr.hasNext()) {
            list.add(itr.next());
        }
        this.iterator = list.iterator();
  }
  
  public boolean hasNext() {
        return this.iterator.hasNext();
  }
  
  public void remove() {
        throw new UnsupportedOperationException("This is a read-only iterator.");
  }
  
  public Object next() {
        return this.iterator.next();
  }
}

枚举器的只读拷贝将用在同步状态上的时间减少到最小,因此可以增强全局的效率。

单线程也有这种异常

当使用 fail-fast iterator 对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,即使是在单线程下运行, java.util.ConcurrentModificationException 异常也将被抛出。
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。
Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照
fail-fast 原则 Iterator 会马上抛出java.util.ConcurrentModificationException 异常。
所以Iterator在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator本身的方法 remove() 来删除对象, Iterator.remove()方法会在删除当前迭代对象的同时维护索引的一致性。
有意思的是如果你的 Collection / Map对象实际只有一个元素的时候, ConcurrentModificationException异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its
correctness: ConcurrentModificationException should be used only to
detect bugs.

import java.util.*;   
public final class MyTest   
{   
    private static HashMap p_mapList = new HashMap(2);
    private MyTest(){}   
    public static void init(){   
    // If only there are more than one element in Map,    
    // the ConcurrentModificationException will not be thrown.   
        p_mapList.put(new String("hello"),new String("world"));   
        p_mapList.put(new String("goto"),new String("hell"));   
    }   
    
    public static void clear() throws Exception{   
        Iterator pTmpKeys = null;   
        Long pTmpKeyLong;       
        pTmpKeys = p_mapList.keySet().iterator();   
        String pCurKey = null;   
        String pCurObj = null;   
        while(pTmpKeys.hasNext()){              
            pCurKey = (String)
            pTmpKeys.next();          
            pCurObj = (String)
            p_mapList.get(pCurKey);          
            p_mapList.put(pCurKey,null);             
            // You can not remove element in Map object directly.              
            //p_mapList.remove(pCurKey);              
            // But you can remove current element by iterator itself.               
            pTmpKeys.remove();               
            System.out.println(pCurKey + "removed.");   
     }         
        System.out.println(p_mapList.size() + " entries left after iterator.");   
        pTmpKeys = null;   
    }   
    
    public static void main(String[] args) throws Exception{
        MyTest.init();      
        MyTest.clear();   
    }   
}