首先,@Graham Borland是正确的。你可以选择使用旧的API,这完全解决了问题。然而,你的软件将不会发展并跟随API的改进,最终将匹配一个不再支持的Android版本。
我要提出的设计模式基于内省,但提供了比@Blundell提出的解决方案更好的编程接口。我认为它足够强大,可以激发对这个常见问题的标准方法。它基于Stack Over Flow和其他论坛的许多帖子。
首先,你需要为你想要实现的服务定义一个接口。你将能够使用你感兴趣的API的不同版本来实现这个服务的不同版本。
事实上,由于我们将在此处共享一些用于加载不同实现的代码,我们选择使用抽象类。它将定义公共方法签名作为接口,但也将提供一个静态方法来加载你的不同实现。
public abstract class MessageManager {
public final static int FOR_MAIL = 0x3689;
public final static int FOR_SMS = 0x3698;
public abstract void pickupContact(int code);
public abstract void sendMessage(int code, Intent data, final String body);
public static MessageManager getInstance( Activity activity )
{
MessageManager instance = null;
try {
Class<? extends MessageManager> messageManagerClass = (Class<? extends MessageManager>) activity.getClassLoader().loadClass( "ca.qc.webalterpraxis.cinedroid.message.MessageManagerSDK7" );
Method singletonMethod = messageManagerClass.getMethod("getInstance", Activity.class );
instance = (MessageManager) singletonMethod.invoke( null , activity);
} catch (Throwable e) {
Log.e( "CinemadroidMain", "Impossible to get an instance of class MessageManagerSDK7",e );
}
return instance;
}
}
然后,您可以使用不同版本的android SDK提供此抽象类的不同实现。
这种方法有些不寻常的是它将工厂设计模式与单例设计模式相结合。所有子类都被要求是单例并提供一个静态的getInstanceMethod。这个抽象类的工厂方法将尝试加载实现此接口的类。如果失败,您可以将要求降级为实现服务的类,并基于旧的APIS。
以下是使用此接口发送邮件和短信的示例类。它是为android sdk 7设计的。
public class MessageManagerSDK7 extends MessageManager {
private static final String LOG_TAG = "MessageManagerSDK7";
private static MessageManagerSDK7 instance = null;
private Activity context;
private MessageManagerSDK7( Activity context )
{
if( instance != null )
throw new RuntimeException( "Should not be called twice. Singleton class.");
this.context = context;
}
public static MessageManagerSDK7 getInstance( Activity context )
{
if( instance == null )
instance = new MessageManagerSDK7( context );
instance.context = context;
return instance;
}
@Override
public void pickupContact( int code )
{
if( code != FOR_MAIL && code != FOR_SMS )
throw new RuntimeException( "Wrong request code, has to be either FOR_MAIL or FOR_SMS.");
Intent intentContact = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
context.startActivityForResult(intentContact, code );
}
@Override
public void sendMessage( int code, Intent data, final String body )
{
if( code != FOR_MAIL && code != FOR_SMS )
throw new RuntimeException( "Wrong request code, has to be either FOR_MAIL or FOR_SMS.");
int icon = 0;
int noItemMessage = 0;
int title = 0;
if( code == FOR_MAIL )
{
icon=R.drawable.mail;
noItemMessage = R.string.no_email_found;
title = R.string.mail_error;
}
else if( code == FOR_SMS )
{
icon=R.drawable.sms;
noItemMessage = R.string.no_number_found;
title = R.string.sms_error;
}
final String[] emailsOrPhoneNumbers = (code == FOR_MAIL ) ? getContactsEmails( data ) : getContactPhoneNumber( data );
if( emailsOrPhoneNumbers == null )
{
new AlertDialog.Builder( context ).setIcon( icon ).setTitle(title).setMessage( noItemMessage ).show();
return;
}
if( emailsOrPhoneNumbers.length > 1 )
{
selectMultipleAndSend( emailsOrPhoneNumbers, body, code);
return;
}
if( code == FOR_MAIL )
sendMail( emailsOrPhoneNumbers, body );
else
sendSMS( emailsOrPhoneNumbers, body );
}
private void sendMail( String[] emails, String body )
{
if( body == null )
{
new AlertDialog.Builder( context ).setIcon( R.drawable.mail ).setTitle(R.string.mail_error).setMessage( R.string.impossible_compose_message ).show();
return;
}
try {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822") ;
i.putExtra(Intent.EXTRA_EMAIL, emails );
i.putExtra(Intent.EXTRA_SUBJECT, context.getString( R.string.showtimes ) );
i.putExtra(Intent.EXTRA_TEXT,body);
context.startActivity(Intent.createChooser(i, context.getString( R.string.select_application ) ) );
} catch (Throwable e) {
new AlertDialog.Builder( context ).setIcon( R.drawable.mail ).setTitle(R.string.mail_error).setMessage( R.string.no_application_mail ).show();
Log.e( LOG_TAG, "No application found", e);
}
}
private void sendSMS( String[] phoneNumbers, String body )
{
try {
Intent sendIntent= new Intent(Intent.ACTION_VIEW);
if( body == null )
{
new AlertDialog.Builder( context ).setIcon( R.drawable.sms ).setTitle(R.string.sms_error).setMessage( R.string.impossible_compose_message ).show();
return;
}
sendIntent.putExtra("sms_body", body);
String phones = "";
for( String phoneNumber : phoneNumbers )
phones += ((phones.length() == 0) ? "" : ";") + phoneNumber;
sendIntent.putExtra("address", phones );
sendIntent.setType("vnd.android-dir/mms-sms");
context.startActivity(sendIntent);
} catch (Throwable e) {
new AlertDialog.Builder( context ).setIcon( R.drawable.sms ).setTitle(R.string.sms_error).setMessage( R.string.no_application_sms ).show();
Log.e( LOG_TAG, "No application found", e);
}
}
protected String[] getContactsEmails(Intent intent)
{
List<String> resultList = new ArrayList<String>();
Cursor cursor = context.managedQuery(intent.getData(), null, null, null, null);
while (cursor.moveToNext())
{
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Cursor emails = context.getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,null,ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,null, null);
while (emails.moveToNext())
{
resultList.add( emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)) );
}
emails.close();
}
cursor.close();
if( resultList.size() == 0 )
return null;
else
return resultList.toArray( new String[ resultList.size() ] );
}
protected String[] getContactPhoneNumber(Intent intent)
{
List<String> resultList = new ArrayList<String>();
Cursor cursor = context.managedQuery(intent.getData(), null, null, null, null);
while (cursor.moveToNext())
{
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
String hasPhone = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
if ( hasPhone.equalsIgnoreCase("1"))
hasPhone = "true";
else
hasPhone = "false" ;
if (Boolean.parseBoolean(hasPhone))
{
Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null);
while (phones.moveToNext())
{
resultList.add( phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) );
}
phones.close();
}
}
cursor.close();
if( resultList.size() == 0 )
return null;
else
return resultList.toArray( new String[ resultList.size() ] );
}
private void selectMultipleAndSend( final String[] emailsOrPhoneNumbers, final String body, final int code )
{
int icon = 0;
int selectMessage = 0;
if( code == FOR_MAIL )
{
icon=R.drawable.mail;
selectMessage = R.string.select_email;
}
else if( code == FOR_SMS )
{
icon=R.drawable.sms;
selectMessage = R.string.select_phone;
}
final boolean[] selected = new boolean[ emailsOrPhoneNumbers.length ];
Arrays.fill( selected, true );
new AlertDialog.Builder( context ).setIcon( icon ).setTitle( selectMessage ).setMultiChoiceItems(emailsOrPhoneNumbers, selected, new OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
selected[ which ] = isChecked;
}
}).setPositiveButton( R.string.OK, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int count = 0;
for( int s=0; s< selected.length; s ++ )
if( selected[s] )
count ++;
String[] selectedEmailsOrPhoneNumbers = new String[ count ];
int index = 0;
for( int s=0; s< selected.length; s ++ )
if( selected[s] )
selectedEmailsOrPhoneNumbers[ index ++ ] = emailsOrPhoneNumbers[ s ];
if( code == FOR_MAIL )
sendMail( selectedEmailsOrPhoneNumbers, body );
else if( code == FOR_SMS )
sendSMS( selectedEmailsOrPhoneNumbers, body );
}
}).setNegativeButton( R.string.cancel , null ).show();
}
}
你还可以提供其他替代方案。尝试按降序加载它们,即从较高的Android版本号开始。
使用您的消息服务非常简单:
MessageManager messageManager = MessageManager.getInstance( this )
如果为null,则没有匹配的服务。如果不为null,则使用MessageManager定义的接口。
通过包含实现基于的版本号,并构建一个小型总线以正确顺序加载类,可以扩展甚至使其更加清晰。
欢迎所有反馈。
敬礼,
Stéphane