我目前正在测试我的InApp计费机制(使用InApp Billing版本3 API,因此将TrivialDrive示例作为参考).
我有一个托管项目,即升级到高级版本.
现在,使用我的测试帐户购买该项目是有效的,但是当我之后在Google结帐中取消整个订单时,我的代码仍然告诉我该项目已购买,因此授予高级功能.
以下是我在MainActivity中检查购买的方法.我不会在某处本地保存购买状态,因为据我所知,使用billing API v3,您可以根据需要查询特定购买.
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
iabHelper = new IabHelper(this, Helper.getPKey());
iabHelper.enableDebugLogging(true);
iabHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
@Override
public void onIabSetupFinished(IabResult result) {
Log.d("IAB", "SETUP FINISHED");
if(!result.isSuccess())
{
Log.d("IAB", "SETUP NOT OK");
return;
}
else
Log.d("IAB", "SETUP OK");
iabHelper.queryInventoryAsync(
new QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
Log.d("IAB", "Query inventory finished.");
if (result.isFailure()) {
Log.d("IAB","Failed to query inventory: " + result);
return;
}
Log.d("IAB", "Query inventory …Run Code Online (Sandbox Code Playgroud) 我正处于完成我的第一个应用程序的边缘,剩下的最后一件事就是实现IAP计费,这就是为什么我目前正在阅读相关主题(包括加密,混淆和东西等安全问题).
我的应用程序是免费版本,能够通过IAP升级到完整版本,因此只有一个托管购买项目"溢价".我有几个问题:
在Google IAP API示例(trivialdrivesample)中,总是在MainActivity中检查IAP以查看用户是否购买了高级版本,通过
mHelper.queryInventoryAsync(mGotInventoryListener);
我的第一个问题:这是否意味着用户始终需要在应用启动时拥有互联网/数据连接,才能切换到高级版本?如果用户没有互联网连接怎么办?他会选择我猜的精简版,我觉得这很烦人.
所以我想到了如何在SharedPrefs或app数据库中本地保存isPremium状态.现在,我知道你无法阻止黑客对应用程序进行逆向工程,无论如何,即便如此,因为我没有服务器来进行服务器端验证.
然而,一个人根本无法在某处保存"isPremium"标志,因为这太容易被发现.
所以我在考虑这样的事情:
- 用户购买Premium
- 应用程序获取IMEI/Device-ID,XOR使用硬编码的String键对其进行编码,并将其保存在应用程序数据库中.
现在,当用户再次启动应用程序时:
- App从数据库中获取编码的String,对其进行解码并检查decodeString == IMEI.如果是 - >溢价
- 如果不是,则将调用正常的queryInventoryAsync以查看用户是否购买了premium.
您如何看待这种方法?我知道这不是超级固定,但对我而言,更重要的是用户不会烦恼(比如强制性的互联网连接),而不是应用程序将无法解决(无论如何这是不可能的).你有其他一些提示吗?
另一件事,我目前还不知道,是当用户卸载/重新安装应用程序时如何恢复事务状态.我知道API有一些机制,并且我的数据库可以通过应用程序导出和导入(因此编码的isPremium标志也可以导出/导入).好吧,我猜这将是另一个问题,当时机成熟时;-)
对这种方法的任何想法和评论都是受欢迎的,您认为这是一个很好的解决方案吗?或者我错过了什么/朝着错误的方向前进?
假设我在SQLiteOpenHelper中有一个包含2列的数据库表test_table和相应的创建脚本:
DB_VERSION = 1:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B);
}
Run Code Online (Sandbox Code Playgroud)
这是最初的应用版本1,发布在Play商店中.
过了一会儿,应用程序和使用的数据库都有更新.我想SQLiteOpenHelper类必须像这样调整:
DB_VERSION = 2:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B, COL_C)");
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSql("ALTER TABLE test_table ADD Column COL_C");
}
Run Code Online (Sandbox Code Playgroud)
一段时间后,另一个应用更新:
DB_VERSION = 3:
public void onCreate(SQLiteDatabase db)
{
db.execSql("CREATE table test_table (COL_A, COL_B, COL_C, COL_D)");
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSql("ALTER TABLE test_table ADD Column COL_D"); …Run Code Online (Sandbox Code Playgroud) 我浏览过Stackoverflow并且已经看到这个问题已被提出,但我没有找到任何解决方案.
我有一个带2个按钮的自定义通知.我希望状态栏面板在我按下该通知上的按钮后关闭,但不知道如何.如果我按下通知本身(所以contentIntent),然后面板关闭,这也是我想要的按钮.
这是我的通知代码:
Notification notification = new Notification(R.drawable.icon, "Service running", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.contentIntent = openIntent;
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.notification_layout);
contentView.setOnClickPendingIntent(R.id.button1, stopIntent);
contentView.setOnClickPendingIntent(R.id.button2, addIntent);
notification.contentView = contentView;
notificationManager.notify(NOTIFICATION_ID, notification);
Run Code Online (Sandbox Code Playgroud) 我有一个启动系统覆盖的服务.当叠加层可见时,我希望它是可触摸/检测触摸,但我想保持与它后面的屏幕的交互(后面我的意思是底层活动,例如叠加层旁边).
我的叠加层是128x128px的位图/ PNG.它被绘制,当我点击它时,我收到了触摸!记录,这很好.但是当我点击屏幕的任何其他部分时(除了叠加层)我也会接触到!记录并且没有与它下面的屏幕的交互是可能的.
以下是我的代码部分:
主要活动启动服务
buttonStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// start Service and return to Homescreen
Intent i = new Intent(MainActivity.this, MyService.class);
startService(i);
Intent newActivity = new Intent(Intent.ACTION_MAIN);
newActivity.addCategory(Intent.CATEGORY_HOME);
newActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newActivity);
}
});
Run Code Online (Sandbox Code Playgroud)
服务/显示覆盖
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
myOverlay = new Overlay(MyService.this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(myOverlay, params);
myOverlay.setAnimation(new Waiting(MyService.this, 0, 0)); …Run Code Online (Sandbox Code Playgroud) 我正在构建一个应用程序,为此我有一个函数来填充测试数据.简短说明:
HashMap<String, Long> iIDs = new HashMap<String, Long>();
HashMap<String, Integer> vals = new HashMap<String, Integer>();
long iID1 = addIndicator("I1", "i1", Color.RED);
long iID2 = addIndicator("I2", "i2", Color.BLUE);
long iID3 = addIndicator("I3", "i3", Color.GREEN);
long iID4 = addIndicator("I4", "i4", Color.MAGENTA);
iIDs.put("iID1", iID1);
iIDs.put("iID2", iID2);
iIDs.put("iID3", iID3);
iIDs.put("iID4", iID4);
int v1 = 80;
int v2 = 30;
int v3 = 25;
int v4 = 40;
vals.put("v1", v1);
vals.put("v2", v2);
vals.put("v3", v3);
vals.put("v4", v4);
int numDays = 500;
int dateDistance = 14;
Calendar …Run Code Online (Sandbox Code Playgroud) 我在WebView的帮助下创建了一个基本的浏览器.
当我访问了一个网站(包括一些文本和一些图片),在/data/data/com.mayexample/cache/webViewCacheChromium缓存目录中被充满了所谓的指数,Data_0的,数据1,数据,f_00001,f_00002等几个文件.
我想知道,这些文件的格式是什么,它们包含什么?我想"所以,这些文件中的一些肯定必须是网站的图像然后"并尝试在文件管理器中打开它们(打开图像).但无论我选择什么文件,该过程都会显示"加载失败!".即使我将其中一些重命名为.jpg,我仍然无法打开任何东西.我在互联网上看到这对某些人有用(看看 Android WebView - 从缓存加载图像,这正是我想要做的),但我对缓存的文件无能为力.
你知道打开webview缓存文件的方法吗?例如,一个文件不代表相应的缓存图像吗?
我真正想要实现的目标(一旦我理解了缓存文件的结构)就是以编程方式从它的缓存中获取webview的图像,就像上面发布的链接的作者一样(不幸的是这个帖子的答案没有多大帮助)
谢谢!
我正在尝试创建一个宏来生成一个可以从 postgres 数据库填充的结构。现在,由于数据库中存在可为空和不可为空的字段,我希望在宏中以不同的方式处理它们。
输出应该是这样的结构:
#[derive(Debug, Default)]
pub struct MyStruct {
pub attrib_a: i64,
pub attrib_b: Option<i64>,
}
impl MyStruct {
pub fn get_row(&self, row: &postgres::rows::Row) -> MyStruct {
MyStruct {
// the non-nullable attrib_a, where I can for sure take the value out of the Option and assign it
attrib_a: match row.get::<_, Option<i64>>("attrib_a") {
Some(x) => x,
None => 0,
},
// here for the nullable attrib_b I just want to return the Option as is
attrib_b: row.get::<_, Option<i64>>("attrib_b"),
} …Run Code Online (Sandbox Code Playgroud) 假设我有一个
private static Object lock = new Object();
Run Code Online (Sandbox Code Playgroud)
在同一个班级的某个地方
public void run() {
synchronized(lock) {
//only 1 thread here, all others wait
}
}
Run Code Online (Sandbox Code Playgroud)
我总是读到锁对象应该是最终的,但不知道为什么。假设我 100% 知道我的代码不会在任何地方触及锁定对象,如果省略了final关键字,这是否意味着同步不是100%防弹的?