android手机开发之网络多线程断点续传代码

 更新时间:2016年9月20日 20:00  点击:2092
本文章介绍一下在手机上开的断点续传功能,有需要的同学可以参考一下下。

本示例介绍在Android平台下通过HTTP协议实现断点续传下载。

我们编写的是Andorid的HTTP协议多线程断点下载应用程序。直接使用单线程下载HTTP文件对我们来说是一件非常简单的事。那么,多线程断点需要什么功能?难在哪里?

1.多线程下载,

2.支持断点。

 

使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是:

  (1)首先获得下载文件的长度,然后设置本地文件的长度。

     HttpURLConnection.getContentLength();//获取下载文件的长度

    RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");

       file.setLength(filesize);//设置本地文件的长度

 

  (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

     如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。

例如10M大小,使用3个线程来下载,

        线程下载的数据长度   (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M

        下载开始位置:线程id*每条线程下载的数据长度 = ?

        下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

 

  (3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,

        如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止

           代码如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");

 

  (4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

 代码如下 复制代码

RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");

threadfile.seek(2097152);//从文件的什么位置开始写入数据

  程序结构如下图所示:

   string.xml文件中代码:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, MainActivity!</string>
    <string name="app_name">Android网络多线程断点下载</string>
    <string name="path">下载路径</string>
    <string name="downloadbutton">下载</string>
    <string name="sdcarderror">SDCard不存在或者写保护</string>
    <string name="success">下载完成</string>
    <string name="error">下载失败</string>
</resources>

 main.xml文件中代码:


     AndroidManifest.xml文件中代码:

 代码如下 复制代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.android.downloader"      android:versionCode="1"      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
   
    <!-- 在SDCard中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
   
    <!-- 往SDCard写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   
    <!-- 访问internet权限 -->
    <uses-permission android:name="android.permission.INTERNET"/>


</manifest> 


 MainActivity中代码:

 代码如下 复制代码

package com.android.downloader;
import java.io.File;

import com.android.network.DownloadProgressListener;
import com.android.network.FileDownloader;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private EditText downloadpathText;
    private TextView resultView;
    private ProgressBar progressBar;
   
    /**
     * 当Handler被创建会关联到创建它的当前线程的消息队列,该类用于往消息队列发送消息
     * 消息队列中的消息由当前线程内部进行处理
     */
    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {           
            switch (msg.what) {
            case 1:               
                progressBar.setProgress(msg.getData().getInt("size"));
                float num = (float)progressBar.getProgress()/(float)progressBar.getMax();
                int result = (int)(num*100);
                resultView.setText(result+ "%");
               
                if(progressBar.getProgress()==progressBar.getMax()){
                    Toast.makeText(MainActivity.this, R.string.success, 1).show();
                }
                break;
            case -1:
                Toast.makeText(MainActivity.this, R.string.error, 1).show();
                break;
            }
        }
    };
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        downloadpathText = (EditText) this.findViewById(R.id.path);
        progressBar = (ProgressBar) this.findViewById(R.id.downloadbar);
        resultView = (TextView) this.findViewById(R.id.resultView);
        Button button = (Button) this.findViewById(R.id.button);
       
        button.setOnClickListener(new View.OnClickListener() {
           
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String path = downloadpathText.getText().toString();
                System.out.println(Environment.getExternalStorageState()+"------"+Environment.MEDIA_MOUNTED);
               
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                    download(path, Environment.getExternalStorageDirectory());
                }else{
                    Toast.makeText(MainActivity.this, R.string.sdcarderror, 1).show();
                }
            }
        });
    }
   
      /**
       * 主线程(UI线程)
       * 对于显示控件的界面更新只是由UI线程负责,如果是在非UI线程更新控件的属性值,更新后的显示界面不会反映到屏幕上
       * @param path
       * @param savedir
       */
    private void download(final String path, final File savedir) {
        new Thread(new Runnable() {           
            @Override
            public void run() {
                FileDownloader loader = new FileDownloader(MainActivity.this, path, savedir, 3);
                progressBar.setMax(loader.getFileSize());//设置进度条的最大刻度为文件的长度
               
                try {
                    loader.download(new DownloadProgressListener() {
                        @Override
                        public void onDownloadSize(int size) {//实时获知文件已经下载的数据长度
                            Message msg = new Message();
                            msg.what = 1;
                            msg.getData().putInt("size", size);
                            handler.sendMessage(msg);//发送消息
                        }
                    });
                } catch (Exception e) {
                    handler.obtainMessage(-1).sendToTarget();
                }
            }
        }).start();
    }


 

DBOpenHelper中代码:

 代码如下 复制代码

package com.android.service;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBOpenHelper extends SQLiteOpenHelper {
    private static final String DBNAME = "down.db";
    private static final int VERSION = 1;
   
    public DBOpenHelper(Context context) {
        super(context, DBNAME, null, VERSION);
    }
   
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS filedownlog");
        onCreate(db);
    }
}

  

FileService中代码:

 代码如下 复制代码

package com.android.service;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class FileService {
    private DBOpenHelper openHelper;

    public FileService(Context context) {
        openHelper = new DBOpenHelper(context);
    }
   
    /**
     * 获取每条线程已经下载的文件长度
     * @param path
     * @return
     */
    public Map<Integer, Integer> getData(String path){
        SQLiteDatabase db = openHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path});
        Map<Integer, Integer> data = new HashMap<Integer, Integer>();
       
        while(cursor.moveToNext()){
            data.put(cursor.getInt(0), cursor.getInt(1));
        }
       
        cursor.close();
        db.close();
        return data;
    }
   
    /**
     * 保存每条线程已经下载的文件长度
     * @param path
     * @param map
     */
    public void save(String path,  Map<Integer, Integer> map){//int threadid, int position
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.beginTransaction();
       
        try{
            for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)",
                        new Object[]{path, entry.getKey(), entry.getValue()});
            }
            db.setTransactionSuccessful();
        }finally{
            db.endTransaction();
        }
       
        db.close();
    }
   
    /**
     * 实时更新每条线程已经下载的文件长度
     * @param path
     * @param map
     */
    public void update(String path, Map<Integer, Integer> map){
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.beginTransaction();
       
        try{
            for(Map.Entry<Integer, Integer> entry : map.entrySet()){
                db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?",
                        new Object[]{entry.getValue(), path, entry.getKey()});
            }
           
            db.setTransactionSuccessful();
        }finally{
            db.endTransaction();
        }
       
        db.close();
    }
   
    /**
     * 当文件下载完成后,删除对应的下载记录
     * @param path
     */
    public void delete(String path){
        SQLiteDatabase db = openHelper.getWritableDatabase();
        db.execSQL("delete from filedownlog where downpath=?", new Object[]{path});
        db.close();
    }


DownloadProgressListener中代码:

package com.android.network;
public interface DownloadProgressListener {
    public void onDownloadSize(int size);


 

FileDownloader中代码:

 代码如下 复制代码

package com.android.network;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.android.service.FileService;

import android.content.Context;
import android.util.Log;

public class FileDownloader {
    private static final String TAG = "FileDownloader";
    private Context context;
    private FileService fileService;   
   
    /* 已下载文件长度 */
    private int downloadSize = 0;
   
    /* 原始文件长度 */
    private int fileSize = 0;
   
    /* 线程数 */
    private DownloadThread[] threads;
   
    /* 本地保存文件 */
    private File saveFile;
   
    /* 缓存各线程下载的长度*/
    private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();
   
    /* 每条线程下载的长度 */
    private int block;
   
    /* 下载路径  */
    private String downloadUrl;
   
    /**
     * 获取线程数
     */
    public int getThreadSize() {
        return threads.length;
    }
   
    /**
     * 获取文件大小
     * @return
     */
    public int getFileSize() {
        return fileSize;
    }
   
    /**
     * 累计已下载大小
     * @param size
     */
    protected synchronized void append(int size) {
        downloadSize += size;
    }
   
    /**
     * 更新指定线程最后下载的位置
     * @param threadId 线程id
     * @param pos 最后下载的位置
     */
    protected synchronized void update(int threadId, int pos) {
        this.data.put(threadId, pos);
        this.fileService.update(this.downloadUrl, this.data);
    }
   
    /**
     * 构建文件下载器
     * @param downloadUrl 下载路径
     * @param fileSaveDir 文件保存目录
     * @param threadNum 下载线程数
     */
    public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) {
        try {
            this.context = context;
            this.downloadUrl = downloadUrl;
            fileService = new FileService(this.context);
            URL url = new URL(this.downloadUrl);
            if(!fileSaveDir.exists()) fileSaveDir.mkdirs();
            this.threads = new DownloadThread[threadNum];                   
           
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5*1000);
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Referer", downloadUrl);
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.connect();
            printResponseHeader(conn);
           
            if (conn.getResponseCode()==200) {
                this.fileSize = conn.getContentLength();//根据响应获取文件大小
                if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");
                       
                String filename = getFileName(conn);//获取文件名称
                this.saveFile = new File(fileSaveDir, filename);//构建保存文件
                Map<Integer, Integer> logdata = fileService.getData(downloadUrl);//获取下载记录
               
                if(logdata.size()>0){//如果存在下载记录
                    for(Map.Entry<Integer, Integer> entry : logdata.entrySet())
                        data.put(entry.getKey(), entry.getValue());//把各条线程已经下载的数据长度放入data中
                }
               
                if(this.data.size()==this.threads.length){//下面计算所有线程已经下载的数据长度
                    for (int i = 0; i < this.threads.length; i++) {
                        this.downloadSize += this.data.get(i+1);
                    }
                   
                    print("已经下载的长度"+ this.downloadSize);
                }
               
                //计算每条线程下载的数据长度
                this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1;
            }else{
                throw new RuntimeException("server no response ");
            }
        } catch (Exception e) {
            print(e.toString());
            throw new RuntimeException("don't connection this url");
        }
    }
   
    /**
     * 获取文件名
     * @param conn
     * @return
     */
    private String getFileName(HttpURLConnection conn) {
        String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1);
       
        if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称
            for (int i = 0;; i++) {
                String mine = conn.getHeaderField(i);
               
                if (mine == null) break;
               
                if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){
                    Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase());
                    if(m.find()) return m.group(1);
                }
            }
           
            filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名
        }
       
        return filename;
    }
   
    /**
     *  开始下载文件
     * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null
     * @return 已下载文件大小
     * @throws Exception
     */
    public int download(DownloadProgressListener listener) throws Exception{
        try {
            RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw");
            if(this.fileSize>0) randOut.setLength(this.fileSize);
            randOut.close();
            URL url = new URL(this.downloadUrl);
           
            if(this.data.size() != this.threads.length){
                this.data.clear();
               
                for (int i = 0; i < this.threads.length; i++) {
                    this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0
                }
            }
           
            for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载
                int downLength = this.data.get(i+1);
               
                if(downLength < this.block && this.downloadSize<this.fileSize){//判断线程是否已经完成下载,否则继续下载   
                    this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
                    this.threads[i].setPriority(7);
                    this.threads[i].start();
                }else{
                    this.threads[i] = null;
                }
            }
           
            this.fileService.save(this.downloadUrl, this.data);
            boolean notFinish = true;//下载未完成
           
            while (notFinish) {// 循环判断所有线程是否完成下载
                Thread.sleep(900);
                notFinish = false;//假定全部线程下载完成
               
                for (int i = 0; i < this.threads.length; i++){
                    if (this.threads[i] != null && !this.threads[i].isFinish()) {//如果发现线程未完成下载
                        notFinish = true;//设置标志为下载没有完成
                       
                        if(this.threads[i].getDownLength() == -1){//如果下载失败,再重新下载
                            this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);
                            this.threads[i].setPriority(7);
                            this.threads[i].start();
                        }
                    }
                }   
               
                if(listener!=null) listener.onDownloadSize(this.downloadSize);//通知目前已经下载完成的数据长度
            }
           
            fileService.delete(this.downloadUrl);
        } catch (Exception e) {
            print(e.toString());
            throw new Exception("file download fail");
        }
        return this.downloadSize;
    }
   
    /**
     * 获取Http响应头字段
     * @param http
     * @return
     */
    public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) {
        Map<String, String> header = new LinkedHashMap<String, String>();
       
        for (int i = 0;; i++) {
            String mine = http.getHeaderField(i);
            if (mine == null) break;
            header.put(http.getHeaderFieldKey(i), mine);
        }
       
        return header;
    }
   
    /**
     * 打印Http头字段
     * @param http
     */
    public static void printResponseHeader(HttpURLConnection http){
        Map<String, String> header = getHttpResponseHeader(http);
       
        for(Map.Entry<String, String> entry : header.entrySet()){
            String key = entry.getKey()!=null ? entry.getKey()+ ":" : "";
            print(key+ entry.getValue());
        }
    }

    private static void print(String msg){
        Log.i(TAG, msg);
    }
}


DownloadThread 中代码:

 代码如下 复制代码

package com.android.network;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.util.Log;

public class DownloadThread extends Thread {
    private static final String TAG = "DownloadThread";
    private File saveFile;
    private URL downUrl;
    private int block;
   
    /* 下载开始位置  */
    private int threadId = -1;   
    private int downLength;
    private boolean finish = false;
    private FileDownloader downloader;
   
    public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) {
        this.downUrl = downUrl;
        this.saveFile = saveFile;
        this.block = block;
        this.downloader = downloader;
        this.threadId = threadId;
        this.downLength = downLength;
    }
   
    @Override
    public void run() {
        if(downLength < block){//未下载完成
            try {
                HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();
                http.setConnectTimeout(5 * 1000);
                http.setRequestMethod("GET");
                http.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
                http.setRequestProperty("Accept-Language", "zh-CN");
                http.setRequestProperty("Referer", downUrl.toString());
                http.setRequestProperty("Charset", "UTF-8");
                int startPos = block * (threadId - 1) + downLength;//开始位置
                int endPos = block * threadId -1;//结束位置
                http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围
                http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
                http.setRequestProperty("Connection", "Keep-Alive");
               
                InputStream inStream = http.getInputStream();
                byte[] buffer = new byte[1024];
                int offset = 0;
                print("Thread " + this.threadId + " start download from position "+ startPos);
                RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");
                threadfile.seek(startPos);
               
                while ((offset = inStream.read(buffer, 0, 1024)) != -1) {
                    threadfile.write(buffer, 0, offset);
                    downLength += offset;
                    downloader.update(this.threadId, downLength);
                    downloader.append(offset);
                }
               
                threadfile.close();
                inStream.close();
                print("Thread " + this.threadId + " download finish");
                this.finish = true;
            } catch (Exception e) {
                this.downLength = -1;
                print("Thread "+ this.threadId+ ":"+ e);
            }
        }
    }
   
    private static void print(String msg){
        Log.i(TAG, msg);
    }
   
    /**
     * 下载是否完成
     * @return
     */
    public boolean isFinish() {
        return finish;
    }
   
    /**
     * 已经下载的内容大小
     * @return 如果返回值为-1,代表下载失败
     */
    public long getDownLength() {
        return downLength;
    }
}


看看在手机上的效果

 

本文介绍在Android平台中实现对XML的三种解析方式。 XML在各种开发中都广泛应用,Android也不例外。作为承载数据的一个重要角色,如何读写XML成为Android开发中一项重要的技能。
  在Android中,常见的XML解析器分别为DOM解析器、SAX解析器和PULL解析器,下面,我将一一向大家详细介绍。
 
第一种方式:DOM解析器:
DOM是基于树形结构的的节点或信息片段的集合,允许开发人员使用DOM API遍历XML树、检索所需数据。分析该结构通常需要加载整个文档和构造树形结构,然后才可以检索和更新节点信息。Android完全支持DOM 解析。利用DOM中的对象,可以对XML文档进行读取、搜索、修改、添加和删除等操作。
DOM的工作原理:使用DOM对XML文件进行操作时,首先要解析文件,将文件分为独立的元素、属性和注释等,然后以节点树的形式在内存中对XML文件进行表示,就可以通过节点树访问文档的内容,并根据需要修改文档——这就是DOM的工作原理。
DOM实现时首先为XML文档的解析定义一组接口,解析器读入整个文档,然后构造一个驻留内存的树结构,这样代码就可以使用DOM接口来操作整个树结构。 
由于DOM在内存中以树形结构存放,因此检索和更新效率会更高。但是对于特别大的文档,解析和加载整个文档将会很耗资源。 当然,如果XML文件的内容比较小,采用DOM是可行的。
常用的DoM接口和类:
  Document:该接口定义分析并创建DOM文档的一系列方法,它是文档树的根,是操作DOM的基础。  
Element:该接口继承Node接口,提供了获取、修改XML元素名字和属性的方法。
Node:该接口提供处理并获取节点和子节点值的方法。
NodeList:提供获得节点个数和当前节点的方法。这样就可以迭代地访问各个节点。
DOMParser:该类是Apache的Xerces中的DOM解析器类,可直接解析XML文件。
下面是DOM的解析流程:
 
 
第二种方式:SAX解析器:
  SAX(Simple API for XML)解析器是一种基于事件的解析器,事件驱动的流式解析方式是,从文件的开始顺序解析到文档的结束,不可暂停或倒退。它的核心是事件处理模式,主要是围绕着事件源以及事件处理器来工作的。当事件源产生事件后,调用事件处理器相应的处理方法,一个事件就可以得到处理。在事件源调用事件处理器中特定方法的时候,还要传递给事件处理器相应事件的状态信息,这样事件处理器才能够根据提供的事件信息来决定自己的行为。  
  SAX解析器的优点是解析速度快,占用内存少。非常适合在Android移动设备中使用。
SAX的工作原理:SAX的工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
  在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通过parser()方法来解析XML文档,并产生事件。事件处理器是org.xml.sax包中ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口。XMLReader通过相应事件处理器注册方法setXXXX()来完成的与ContentHander、DTDHander、ErrorHandler,以及EntityResolver这4个接口的连接。
常用的SAX接口和类:
Attrbutes:用于得到属性的个数、名字和值。  
ContentHandler:定义与文档本身关联的事件(例如,开始和结束标记)。大多数应用程序都注册这些事件。
DTDHandler:定义与DTD关联的事件。它没有定义足够的事件来完整地报告DTD。如果需要对DTD进行语法分析,请使用可选的DeclHandler。
DeclHandler是SAX的扩展。不是所有的语法分析器都支持它。
EntityResolver:定义与装入实体关联的事件。只有少数几个应用程序注册这些事件。
ErrorHandler:定义错误事件。许多应用程序注册这些事件以便用它们自己的方式报错。
DefaultHandler:它提供了这些接LI的缺省实现。在大多数情况下,为应用程序扩展DefaultHandler并覆盖相关的方法要比直接实现一个接口更容易。
 详见下表:
 
  
 可知,我们需要XmlReader 以及DefaultHandler来配合解析xml。
下面是SAX的解析流程:
 
 
 
第三种方式:PULL解析器: 
      Android并未提供对Java StAX API的支持。但是,Android附带了一个pull解析器,其工作方式类似于StAX。它允许用户的应用程序代码从解析器中获取事件,这与SAX解析器自动将事件推入处理程序相反。 
PULL解析器的运行方式和SAX类似,都是基于事件的模式。不同的是,在PULL解析过程中返回的是数字,且我们需要自己获取产生的事件然后做相应的操作,而不像SAX那样由处理器触发一种事件的方法,执行我们的代码。
        读取到xml的声明返回 START_DOCUMENT;
 代码如下 复制代码
读取到xml的结束返回 END_DOCUMENT ;
读取到xml的开始标签返回 START_TAG
读取到xml的结束标签返回 END_TAG
读取到xml的文本返回 TEXT
  
PULL解析器小巧轻便,解析速度快,简单易用,非常适合在Android移动设备中使用,Android系统内部在解析各种XML时也是用PULL解析器,Android官方推荐开发者们使用Pull解析技术。Pull解析技术是第三方开发的开源技术,它同样可以应用于JavaSE开发。
      PULL 的工作原理:XML pull提供了开始元素和结束元素。当某个元素开始时,我们可以调用parser.nextText从XML文档中提取所有字符数据。当解释到一个文档结束时,自动生成EndDocument事件。
      常用的XML pull的接口和类: 
XmlPullParser:XML pull解析器是一个在XMLPULL VlAP1中提供了定义解析功能的接口。
XmlSerializer:它是一个接口,定义了XML信息集的序列。
XmlPullParserFactory:这个类用于在XMPULL V1 API中创建XML Pull解析器。
XmlPullParserException:抛出单一的XML pull解析器相关的错误。
PULL的解析流程如下:
      [附加]第四种方式: Android.util.Xml类
在Android API中,另外提供了Android.util.Xml类,同样可以解析XML文件,使用方法类似SAX,也都需编写Handler来处理XML的解析,但是在使用上却比SAX来得简单 ,如下所示:
以android.util.XML实现XML解析 ,
 代码如下 复制代码
MyHandler myHandler=new MyHandler0;

android.util.Xm1.parse(ur1.openC0nnection().getlnputStream0,Xm1.Encoding.UTF-8,myHandler);

 下面是一个参考文档river.xml,放在assets目录.如下:
 代码如下 复制代码
<?xml version="1.0" encoding="utf-8"?>
<rivers>
 <river name="灵渠" length="605">
     <introduction>
      灵渠在广西壮族自治区兴安县境内,是世界上最古老的运河之一,有着“世界古代水利建筑明珠”的美誉。灵渠古称秦凿渠、零渠、陡河、兴安运河,于公元前214年凿成通航,距今已2217年,仍然发挥着功用。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>

   
   <river name="胶莱运河" length="200">
     <introduction>
      胶莱运河南起黄海灵山海口,北抵渤海三山岛,流经现胶南、胶州、平度、高密、昌邑和莱州等,全长200公里,流域面积达5400平方公里,南北贯穿山东半岛,沟通黄渤两海。胶莱运河自平度姚家村东的分水岭南北分流。南流由麻湾口入胶州湾,为南胶莱河,长30公里。北流由海仓口入莱州湾,为北胶莱河,长100余公里。
   

 代码如下 复制代码
  </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
  
   <river name="苏北灌溉总渠" length="168">
     <introduction>
      位于淮河下游江苏省北部,西起洪泽湖边的高良涧,流经洪泽,青浦、淮安,阜宁、射阳,滨海等六县(区),东至扁担港口入海的大型人工河道。全长168km。
     </introduction>
      <imageurl>
      http://imgsrc.baidu.com/baike/pic/item/389aa8fdb7b8322e08244d3c.jpg
     </imageurl>
   </river>
 </rivers>


   
      采用DOM解析时具体处理步骤是:

1 首先利用DocumentBuilderFactory创建一个DocumentBuilderFactory实例
2 然后利用DocumentBuilderFactory创建DocumentBuilder

3 然后加载XML文档(Document),
4 然后获取文档的根结点(Element),
5 然后获取根结点中所有子节点的列表(NodeList),
6 然后使用再获取子节点列表中的需要读取的结点。

  当然我们观察节点,我需要用一个River对象来保存数据,抽象出River类

 代码如下 复制代码
public class River implements Serializable {    
   privatestaticfinallong serialVersionUID = 1L;
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public String getIntroduction() {
        return introduction;
    }
    public void setIntroduction(String introduction) {
        this.introduction = introduction;
    }
    public String getImageurl() {
        return imageurl;
    }
    public void setImageurl(String imageurl) {
        this.imageurl = imageurl;
    }
    private int length;
    private String introduction;
    private String imageurl;
}


 

下面我们就开始读取xml文档对象,并添加进List中:

代码如下: 我们这里是使用assets中的river.xml文件,那么就需要读取这个xml文件,返回输入流。 读取方法为:inputStream=this.context.getResources().getAssets().open(fileName); 参数是xml文件路径,当然默认的是assets目录为根目录。

然后可以用DocumentBuilder对象的parse方法解析输入流,并返回document对象,然后再遍历doument对象的节点属性。
 

   //获取全部河流数据

 

 代码如下 复制代码

  /**  

     * 参数fileName:为xml文档路径
     */
    public List<River> getRiversFromXml(String fileName){
        List<River> rivers=new ArrayList<River>();
        DocumentBuilderFactory factory=null;
        DocumentBuilder builder=null;
        Document document=null;
        InputStream inputStream=null;
        //首先找到xml文件
        factory=DocumentBuilderFactory.newInstance();
        try {
            //找到xml,并加载文档
            builder=factory.newDocumentBuilder();
            inputStream=this.context.getResources().getAssets().open(fileName);
            document=builder.parse(inputStream);
            //找到根Element
             Element root=document.getDocumentElement();
             NodeList nodes=root.getElementsByTagName(RIVER);
            //遍历根节点所有子节点,rivers 下所有river
             River river=null;
             for(int i=0;i<nodes.getLength();i++){
                     river=new River();
                     //获取river元素节点
                     Element riverElement=(Element)(nodes.item(i));
                     //获取river中name属性值
                     river.setName(riverElement.getAttribute(NAME));
                     river.setLength(Integer.parseInt(riverElement.getAttribute(LENGTH)));
                     //获取river下introduction标签
                     Element introduction=(Element)riverElement.getElementsByTagName(INTRODUCTION).item(0);
                     river.setIntroduction(introduction.getFirstChild().getNodeValue());
                     Element imageUrl=(Element)riverElement.getElementsByTagName(IMAGEURL).item(0);
                     river.setImageurl(imageUrl.getFirstChild().getNodeValue());
                 rivers.add(river);
             }
        }catch (IOException e){
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }
         catch (ParserConfigurationException e) {
            e.printStackTrace();
        }finally{
            try {
                inputStream.close();
            } catch (IOException e) {   
                e.printStackTrace();
            }
        }
        return rivers;
    }

 

在这里添加到List中, 然后我们使用ListView将他们显示出来。如图所示:


  采用SAX解析时具体处理步骤是:

1 创建SAXParserFactory对象

2 根据SAXParserFactory.newSAXParser()方法返回一个SAXParser解析器

3 根据SAXParser解析器获取事件源对象XMLReader

4 实例化一个DefaultHandler对象

5 连接事件源对象XMLReader到事件处理类DefaultHandler中

6 调用XMLReader的parse方法从输入源中获取到的xml数据

7 通过DefaultHandler返回我们需要的数据集合。

 

代码如下:

 代码如下 复制代码

  public List<River> parse(String xmlPath){
List<River> rivers=null;
        SAXParserFactory factory=SAXParserFactory.newInstance();
        try {
            SAXParser parser=factory.newSAXParser();
            //获取事件源
            XMLReader xmlReader=parser.getXMLReader();
            //设置处理器
            RiverHandler handler=new RiverHandler();
            xmlReader.setContentHandler(handler);
            //解析xml文档
            //xmlReader.parse(new InputSource(new URL(xmlPath).openStream()));
            xmlReader.parse(new InputSource(this.context.getAssets().open(xmlPath)));
            rivers=handler.getRivers();   
        } catch (ParserConfigurationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        return rivers;
    }


 

重点在于DefaultHandler对象中对每一个元素节点,属性,文本内容,文档内容进行处理。

前面说过DefaultHandler是基于事件处理模型的,基本处理方式是:当SAX解析器导航到文档开始标签时回调startDocument方法,导航到文档结束标签时回调endDocument方法。当SAX解析器导航到元素开始标签时回调startElement方法,导航到其文本内容时回调characters方法,导航到标签结束时回调endElement方法。

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到文档开始标签时,在回调函数startDocument中,可以不做处理,当然你可以验证下UTF-8等等。

2:当导航到rivers开始标签时,在回调方法startElement中可以实例化一个集合用来存贮list,不过我们这里不用,因为在构造函数中已经实例化了。

3:导航到river开始标签时,就说明需要实例化River对象了,当然river标签中还有name ,length属性,因此实例化River后还必须取出属性值,attributes.getValue(NAME),同时赋予river对象中,同时添加为导航到的river标签添加一个boolean为真的标识,用来说明导航到了river元素。

4:当然有river标签内还有子标签(节点),但是SAX解析器是不知道导航到什么标签的,它只懂得开始,结束而已。那么如何让它认得我们的各个标签呢?当然需要判断了,于是可以使用回调方法startElement中的参数String localName,把我们的标签字符串与这个参数比较下,就可以了。我们还必须让SAX知道,现在导航到的是某个标签,因此添加一个true属性让SAX解析器知道。

5:它还会导航到文本内标签,(就是<img></img>里面的内容),回调方法characters,我们一般在这个方法中取出就是<img></img>里面的内容,并保存。 6:当然它是一定会导航到结束标签</river> 或者</rivers>的,如果是</river>标签,记得把river对象添加进list中。如果是river中的子标签</introduction>,就把前面设置标记导航到这个标签的boolean标记设置为false. 按照以上实现思路,可以实现如下代码:


  /**导航到开始标签触发**/
        publicvoid startElement (String uri, String localName, String qName, Attributes attributes){
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
         //如果读取的是river标签开始,则实例化River
         if(tagName.equals(RIVER)){
             isRiver=true;
             river=new River();
                /**导航到river开始节点后**/
                river.setName(attributes.getValue(NAME));
                river.setLength(Integer.parseInt(attributes.getValue(LENGTH)));
         }
         //然后读取其他节点
          if(isRiver){
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=true;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=true;
             } 
         } 
        }
       
        /**导航到结束标签触发**/
        public void endElement (String uri, String localName, String qName){
         String tagName=localName.length()!=0?localName:qName;
         tagName=tagName.toLowerCase().trim();
        
        //如果读取的是river标签结束,则把River添加进集合中
         if(tagName.equals(RIVER)){
             isRiver=true;
             rivers.add(river);
         }
         //然后读取其他节点
          if(isRiver){
              if(tagName.equals(INTRODUCTION)){
                 xintroduction=false;
             }else if(tagName.equals(IMAGEURL)){
                 ximageurl=false;
             }
          }  
        }
       
        //这里是读取到节点内容时候回调
        public void characters (char[] ch, int start, int length){
            //设置属性值
                if(xintroduction){
                     //解决null问题
                     river.setIntroduction(river.getIntroduction()==null?"":river.getIntroduction()+new String(ch,start,length));
                 }else if(ximageurl){
                     //解决null问题
                     river.setImageurl(river.getImageurl()==null?"":river.getImageurl()+new String(ch,start,length));
                 }   
        }


 

运行效果跟上例DOM 运行效果相同。

 

采用PULL解析基本处理方式:

当PULL解析器导航到文档开始标签时就开始实例化list集合用来存贮数据对象。导航到元素开始标签时回判断元素标签类型,如果是river标签,则需要实例化River对象了,如果是其他类型,则取得该标签内容并赋予River对象。当然它也会导航到文本标签,不过在这里,我们可以不用。

根据以上的解释,我们可以得出以下处理xml文档逻辑:

1:当导航到XmlPullParser.START_DOCUMENT,可以不做处理,当然你可以实例化集合对象等等。

2:当导航到XmlPullParser.START_TAG,则判断是否是river标签,如果是,则实例化river对象,并调用getAttributeValue方法获取标签中属性值。

3:当导航到其他标签,比如Introduction时候,则判断river对象是否为空,如不为空,则取出Introduction中的内容,nextText方法来获取文本节点内容

4:当然啦,它一定会导航到XmlPullParser.END_TAG的,有开始就要有结束嘛。在这里我们就需要判读是否是river结束标签,如果是,则把river对象存进list集合中了,并设置river对象为null.

由以上的处理逻辑,我们可以得出以下代码:

 代码如下 复制代码


    public List<River> parse(String xmlPath){
List<River> rivers=new ArrayList<River>();
        River river=null;
        InputStream inputStream=null;   
        //获得XmlPullParser解析器
        XmlPullParser xmlParser = Xml.newPullParser();  
        try {
            //得到文件流,并设置编码方式
            inputStream=this.context.getResources().getAssets().open(xmlPath);
            xmlParser.setInput(inputStream, "utf-8");
            //获得解析到的事件类别,这里有开始文档,结束文档,开始标签,结束标签,文本等等事件。
            int evtType=xmlParser.getEventType();
         //一直循环,直到文档结束   
         while(evtType!=XmlPullParser.END_DOCUMENT){
            switch(evtType){
            case XmlPullParser.START_TAG:
                String tag = xmlParser.getName();
                //如果是river标签开始,则说明需要实例化对象了
                if (tag.equalsIgnoreCase(RIVER)) {
                   river = new River();
                  //取出river标签中的一些属性值
                  river.setName(xmlParser.getAttributeValue(null, NAME));
                  river.setLength(Integer.parseInt(xmlParser.getAttributeValue(null, LENGTH)));
                }else if(river!=null){
                    //如果遇到introduction标签,则读取它内容
                    if(tag.equalsIgnoreCase(INTRODUCTION)){
                    river.setIntroduction(xmlParser.nextText());
                    }else if(tag.equalsIgnoreCase(IMAGEURL)){
                        river.setImageurl(xmlParser.nextText());
                    }
                }
                break;
               
           case XmlPullParser.END_TAG:
             //如果遇到river标签结束,则把river对象添加进集合中
               if (xmlParser.getName().equalsIgnoreCase(RIVER) && river != null) {
                   rivers.add(river);
                   river = null;
               }
                break;
                default:break;
            }
            //如果xml没有结束,则导航到下一个river节点
            evtType=xmlParser.next();
         }
        } catch (XmlPullParserException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        return rivers;


 }

运行效果和上面的一样。

 

本文章介绍了关于android手机开发之自动开关机实现代码,有需要学习的朋友可以参考一下。

我的实现是在设置程序里面增加一个接口,让用户设置自动开关机,这个自动开关机的设置可以参照闹钟的设置。关于自动关机,考虑到关机的时候,用户可能正有一些重要的操作,那么应该给用户一个机会去取消当前的关机。

1)一个BroadcastReceiver, 接收如下信息:

a) 自定义的ACTION_REQUEST_POWER_OFF:设置auto power off时,通过AlarmManager设置的一个RTC_WAKEUP时钟。当到设置的关机时间时,之前设置到AlarmManager的这个action会被广播。我们实现的这个BroadcastReceiver接收到这个消息后,就要开始power off流程

b) 自定义的ACTION_REQUEST_POWER_ON:设置auto power on时,通过AlarmManager设置的一个RTC_WAKEUP时钟。我们知道power on的应该设置一个rtc的alarm,那么这个RTC_WAKEUP的alarm是做什么的呢?其实当用户设置自动关机的时候,我设置了2个时钟,一个是RTC时钟,用于关机状态下开机;还有一个就是这个RTC_WAKEUP时钟。之所以设置这个时钟,其实是这样的,比如说你设置了周一到周五每天7点半自动开机,而周四早上你7点就打开了手机,这样到7点半的时候,之前设置的时钟就过期了,如果不重新设置的话,周五早上是不会自动开机的。所以这个时候,之前设置的RTC_WAKEUP就接收到了这样的信息,在重新设置下次自动开机的时钟。

c) BOOT_COMPLETE和TIMEZONE changed, Time set等时间相关的action:当系统开机完成或时间、时区发生改变时,都需要重新设置alarm。

2)一个处理power off 的Service,当BroadcastReceiver接收到ACTION_REQUEST_POWER_OFF,我们给用户一个机会去取消当前的自动关机。这个Service的作用就是启动一个无背景的页面,给用户提示。同时播放之前用户设置的提示音或振动。

3)一个Activity:显示一个dialog提示用户要自动关机,并用一个计时器倒计时。当用户确认关机,或者计时器到时间的时候,就关机。否则取消当前关机,并重设下次自动关机alarm。


2. 自动关机的实现。自动关机的实现比较简单,这里主要说一下怎么设置alarm,和实现关机:

1) 设置自动关机的alarm:

 

 代码如下 复制代码

   AlarmManager am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(
                "com.android.settings.action.REQUEST_POWER_OFF");

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT);
        am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);

2)自动关机掉的是./frameworks/base/services/java/com/android/server/ShutdownActivity.java:

 代码如下 复制代码

        Intent newIntent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(newIntent);

Intent.ACTION_REQUEST_SHUTDOWN是Intent里面一个隐藏的action。

3. 自动开机的实现。一直在做上层应用和framework,对于底层不是很熟悉。正好有同事之前做过关机闹铃,所以把他之前的实现稍加改动就可以了。在系统power off的状态下自动开机,我们需要设置一个rtc时钟,当用户设置自动开机时,由AlarmManagerService将时钟设置下去。这学要底层的支持。这里的实现是定义一个我们自己的rtc alarm type:

1) 首先要在头文件里面定义:

a) kernel/include/linux/android_alarm.h

 代码如下 复制代码

#define ANDROID_ALARM_GET_TIME(type)        ALARM_IOW(4, type, struct timespec)
#define ANDROID_ALARM_SET_RTC               _IOW('a', 5, struct timespec)

/* we define ANDROID_RTC_ALARM_SET for auto power off */
#define ANDROID_RTC_ALARM_SET               _IOW('a', 7, int)

#define ANDROID_ALARM_BASE_CMD(cmd)         (cmd & ~(_IOC(0, 0, 0xf0, 0)))

b) bionic/libc/kernel/common/linux/android_alarm.h

#define ANDROID_RTC_ALARM_SET _IOW('a', 7, int)

2) 定义完成之后,还需要实现:在kernel/drivers/rtc/alarm-dev.c文件的alarm_ioctl方法里面,增加一个case,实现设置alarm

 

 代码如下 复制代码
   case ANDROID_RTC_ALARM_SET:
        {
            unsigned int rtc_alarm_time;
            struct rtc_time rtc_now;
            if (copy_from_user(&rtc_alarm_time, (void __user *)arg,
                sizeof(rtc_alarm_time))) {
                rv = -EFAULT;
                goto err1;
            }
            if (pmic_rtc_get_time(&rtc_now) < 0) {
                rtc_now.sec = 0;
                if (pmic_rtc_start(&rtc_now) < 0) {
                    printk("get and set rtc info failedn");
                    break;
                }
            }
            pmic_rtc_disable_alarm(PM_RTC_ALARM_1);
            rtc_now.sec += rtc_alarm_time;
            pmic_rtc_enable_alarm(PM_RTC_ALARM_1, &rtc_now);
            break;
        }

当然不要忘记增加一个include:

 代码如下 复制代码

#include <mach/pmic.h>

3)在frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp里面增加一个方法去设置时钟:

 代码如下 复制代码

static void android_server_AlarmManagerService_updateRtcAlarm(JNIEnv* env, jobject obj, jint fd, jint seconds)
{
#if HAVE_ANDROID_OS
    int result = ioctl(fd, ANDROID_RTC_ALARM_SET, &seconds);
    LOGE("set rtc alarm to %d later: %sn", seconds, strerror(errno));
    if (result < 0)
    {
        LOGE("Unable to set rtc alarm to %d later: %sn", seconds, strerror(errno));
    }
#endif
}

还有就是不要忘记定义一下接口:

 代码如下 复制代码

{"updateRtcAlarm", "(II)V", (void*)android_server_AlarmManagerService_updateRtcAlarm},

4) 在frameworks/base/services/java/com/android/server/AlarmManagerService.java里面定义native的设置alarm的方法,然后调用就可以实现将自动关机的alarm设置下去了:

定义:private native void updateRtcAlarm(int fd, int seconds);

调用:

 代码如下 复制代码

    public void setRepeating(int type, long triggerAtTime, long interval,
            PendingIntent operation) {
        if (operation == null) {
            Slog.w(TAG, "set/setRepeating ignored because there is no intent");
            return;
        }
        synchronized (mLock) {
            Alarm alarm = new Alarm();
            alarm.type = type;
            alarm.when = triggerAtTime;
            alarm.repeatInterval = interval;
            alarm.operation = operation;

            // Remove this alarm if already scheduled.
            removeLocked(operation);

            if (localLOGV) Slog.v(TAG, "set: " + alarm);

            int index = addAlarmLocked(alarm);
            if (index == 0) {
                setLocked(alarm);
            }

            // Start to setup auto power on alarm
            if ((alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP) &&
                                alarm.operation.getTargetPackage().equals("com.android.settings")) {
                updateRtcAlarm(mDescriptor, (int)((alarm.when - System.currentTimeMillis()) / 1000));
            }
            // End to setup auto power on alarm
        }
    }

5)在应用层设置自动开机

  

 代码如下 复制代码

      AlarmManager am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(
                "com.android.settings.action.REQUEST_POWER_ON");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT);
        am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pendingIntent);


 

4. 总结

1) 自动开机原理比较简单,但是需要底层的支持,所以对于做应用或者framework层的技术人员来说,实现起来稍微比较麻烦。
2) 在设置自动开关机的时候,需要考虑的情况很多,比如是否设置时间/时区的改变,手机当前是开机还是关机状态等。

本文章介绍一下关于在android下生成xml文件并保存到本地的服务器的方法,有需要的同学可以参考一下本文章。
 代码如下 复制代码
private static void XmlFileCreator(List<JokeBean> data){
        File newxmlfile = new File(Environment.getExternalStorageDirectory()+"/new.xml");
        try{
            if(!newxmlfile.exists())
                newxmlfile.createNewFile();
        }catch(IOException e){
            Log.e("IOException", "exception in createNewFile() method");
        }
        //we have to bind the new file with a FileOutputStream
        FileOutputStream fileos = null;       
        try{
            fileos = new FileOutputStream(newxmlfile);
        }catch(FileNotFoundException e){
            Log.e("FileNotFoundException", "can't create FileOutputStream");
        }
        //we create a XmlSerializer in order to write xml data
        XmlSerializer serializer = Xml.newSerializer();
        try {
            //we set the FileOutputStream as output for the serializer, using UTF-8 encoding
            serializer.setOutput(fileos, "UTF-8");
            //Write <?xml declaration with encoding (if encoding not null) and standalone flag (if standalone not null)
            serializer.startDocument(null, Boolean.valueOf(true));
            //set indentation option
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
          //start a tag called "root"
            serializer.startTag(null, "jokes");
            for(JokeBean joke:data){
                serializer.startTag(null, "joke");
                //i indent code just to have a view similar to xml-tree
                serializer.startTag(null, "id");
                serializer.text(joke.getId());
                serializer.endTag(null, "id");
                                  
                serializer.startTag(null, "title");
                serializer.text(joke.getTitle());
                //set an attribute called "attribute" with a "value" for <child2>
                //serializer.attribute(null, "attribute", "value");
                serializer.endTag(null, "title");
                serializer.startTag(null, "text");
                //write some text inside <text>
                serializer.text(joke.getText());
                serializer.endTag(null, "text");
                                  
                serializer.endTag(null, "joke");
            }
            serializer.endTag(null, "jokes");
            serializer.endDocument();
            //write xml data into the FileOutputStream
            serializer.flush();
            //finally we close the file stream
            fileos.close();
        } catch (Exception e) {
            Log.e("Exception","error occurred while creating xml file");
        }
    }
本文章介绍一篇关于安卓开发之通过手势实现图像拖拽功能,有需要的同学可以参考一下本文章哦。

本示例实现Android通过手势可以实现图像的拖拽功能。运行效果如下:

 

 

 代码如下 复制代码

package com.android.drag;import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;

/**
 * Android通过手势实现图像拖拽功能
 * @author Administrator
 *
 */
public class DragActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // setContentView(R.layout.main);
        View view = new ImageDragView(this);
       
        setContentView(view);
    }

    class ImageDragView extends View {

        private float x1;
        private float y1;
        private float x2;
        private float y2;

        public ImageDragView(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub

            float size = event.getSize();

            int szi = (int) size;
            int dxi = szi >> 12;
            int dyit = ((1 << 12) - 1);
            int dyi = szi & dyit;

            DisplayMetrics metrics = getResources().getDisplayMetrics();
           
            float dx = metrics.widthPixels * dxi / (float) dyit;
            float dy = metrics.heightPixels * dyi / (float) dyit;

            x1 = event.getX();
            y1 = event.getY();

            x2 = x1 + dx;
            y2 = y1 + dy;

            invalidate();

            return true;
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);

            float r = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2)
                    * (y1 - y2)) / 2;
            r = 100 >= r ? 100 : r;

            Paint paint = new Paint();
            paint.setColor(Color.RED);
            canvas.drawCircle(x1, y1, r, paint);
        }
    }

 

[!--infotagslink--]

相关文章

  • 安卓手机app添加支付宝支付开发教程

    支付宝支付在国内算是大家了,我们到处都可以使用支付宝了,下文整理介绍的是在安卓app应用中使用支付宝进行支付的开发例子。 之前讲了一篇博客关与支付宝集成获取...2016-09-20
  • PHP+Ajax手机发红包的程序例子

    PHP+Ajax有许多的功能都会用到它小编今天就有使用PHP+Ajax实现的一个微信登录功能了,下面我们来看一个PHP+Ajax手机发红包的程序例子,具体如下所示。 PHP发红包基本...2016-11-25
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • 华为手机怎么恢复已卸载的应用?华为手机恢复应用教程

    华为手机怎么恢复已卸载的应用?有时候我们在使用华为手机的时候,想知道卸载的应用怎么恢复,这篇文章主要介绍了华为手机恢复应用教程,需要的朋友可以参考下...2020-06-29
  • 破解安卓手机上的九宫格锁方法步骤

    很多用安卓智能手机的朋友是用九宫格锁屏,网上也有暴力删除手机图形锁的方法,不过我们可以用程序来破解。本文只提供技术学习,不能干坏事 安卓手机的图形锁(九宫格)...2016-09-20
  • 华为手机怎么开启双时钟? 华为添加双时钟的技巧

    华为手机怎么开启双时钟?华为手机是可以设置双时钟的,如果来回在两个有时差的地方工作,是可以设置双时钟显示,下面我们就来看看华为添加双时钟的技巧,需要的朋友可以参考下...2020-12-08
  • 安卓手机wifi打不开修复教程,安卓手机wifi打不开解决方法

    手机wifi打不开?让小编来告诉你如何解决。还不知道的朋友快来看看。 手机wifi是现在生活中最常用的手机功能,但是遇到手机wifi打不开的情况该怎么办呢?如果手机wifi...2016-12-21
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20